diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index a755cdb..0000000 --- a/.eslintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["standard"] -} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..76b0ad2 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,22 @@ +{ + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "node": true, + "jest": true + }, + "parserOptions": { + "ecmaFeatures": {}, + "sourceType": "module" + }, + "rules": { + "no-const-assign": "warn", + "no-this-before-super": "warn", + "no-undef": "warn", + "no-unreachable": "warn", + "no-unused-vars": "warn", + "constructor-super": "warn", + "valid-typeof": "warn" + } +} diff --git a/.jshintignore b/.jshintignore deleted file mode 100644 index f06235c..0000000 --- a/.jshintignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -dist diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 2732d38..0000000 --- a/.jshintrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "node": true, - "esnext": true, - "esversion": 6, - - "asi": true, - "curly": true, - "latedef": true, - "quotmark": true, - "undef": true, - "unused": true, - "trailing": true -} diff --git a/CHANGELOG.md b/CHANGELOG.md index 94fefcf..0a476ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ A toward 1.0 release +## 0.69.9 + +- Remove standard code style and use prettier + ## 0.69.8 - `dictionary` module uses `pcset.nodes` instead of `pcset.chromaModes` diff --git a/dist/tonal.min.js b/dist/tonal.min.js index 7524112..b427aed 100644 --- a/dist/tonal.min.js +++ b/dist/tonal.min.js @@ -1 +1,2 @@ !function(n,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):n.Tonal=t()}(this,function(){"use strict";function n(n,t){return Array(t+1).join(n)}function t(n){return"number"==typeof n}function r(n,t){return Math.pow(2,(n-69)/12)*(t||440)}function e(n,t,e){if("string"!=typeof n)return null;var u=Ut.exec(n);if(!u||!t&&u[4])return null;var i={letter:u[1].toUpperCase(),acc:u[2].replace(/x/g,"##")};i.pc=i.letter+i.acc,i.step=(i.letter.charCodeAt(0)+3)%7,i.alt="b"===i.acc[0]?-i.acc.length:i.acc.length;var o=$t[i.step]+i.alt;return i.chroma=o<0?12+o:o%12,u[3]&&(i.oct=+u[3],i.midi=o+12*(i.oct+1),i.freq=r(i.midi,e)),t&&(i.tonicOf=u[4]),i}function u(r){return t(r)?r<0?n("b",-r):n("#",r):""}function i(n){return t(n)?""+n:""}function o(n,t,r){return null===n||void 0===n?null:n.step?o(n.step,n.alt,n.oct):n<0||n>6?null:Bt.charAt(n)+u(t)+i(r)}function c(n,t){if("string"!=typeof n)return null;var r=Nt.exec(n);if(!r)return null;var e={num:+(r[3]||r[8]),q:r[4]||r[6]};e.dir="-"===(r[2]||r[7])?-1:1;var u=(e.num-1)%7;return e.simple=u+1,e.type=Tt[u],e.alt=a(e.type,e.q),e.oct=Math.floor((e.num-1)/7),e.size=e.dir*(Vt[u]+e.alt+12*e.oct),!1!==t&&"M"===e.type&&"P"===e.q?null:e}function m(n){return Tt[(n-1)%7]}function a(n,t){var r="number"==typeof n?m(n):n;return"M"===t&&"M"===r?0:"P"===t&&"P"===r?0:"m"===t&&"M"===r?-1:/^A+$/.test(t)?t.length:/^d+$/.test(t)?"P"===r?-t.length:-t.length-1:null}function P(n,t){return Array(Math.abs(t)+1).join(n)}function M(n,t){var r="number"==typeof n?m(Math.abs(n)):n;return 0===t?"M"===r?"M":"P":-1===t&&"M"===r?"m":t>0?P("A",t):t<0?P("d","P"===r?t:t+1):null}function l(n){return"number"==typeof n}function f(n,t,r){var e=Rt[n]+7*t;return l(r)?[e,r-Kt[n]-4*t]:[e]}function s(n){var t=(n+1)%7;return t<0?7+t:t}function p(n,t){var r=Lt[s(n)],e=Math.floor((n+1)/7);return l(t)?[r,e,t+4*e+Kt[r]]:[r,e]}function d(n,t,r){return r?["tnlp",[n,t],r]:["tnlp",[n,t]]}function h(n){return Array.isArray(n)&&"tnlp"===n[0]}function b(n,t,r,e){return e?["tnlp",f(n,t,r),e]:["tnlp",f(n,t,r)]}function v(n){return p.apply(null,n[1])}function A(n){return h(n)?n[2]?"ivl":"note":null}function g(n){return"note"===A(n)}function y(n){return"ivl"===A(n)}function j(n){return h(n)&&1===n[1].length}function x(n){return-1===n[2]?-1:1}function O(n){return-1===n[2]?-n[1][0]:n[1][0]}function w(n){return-1===n[2]?-n[1][1]:n[1][1]}function I(n){return 7*O(n)+12*w(n)}function C(n){var t=O(n);return 7*t-12*Math.floor(7*t/12)}function z(n){var t={};return function(r){return"string"!=typeof r?null:t[r]||(t[r]=n(r))}}function _(n){return Ht(n)||Jt(n)}function q(n){return g(n)?n:Ht(n)}function F(n){return y(n)?n:Jt(n)}function E(n){return h(n)?n:_(n)}function G(n){return g(n)?o.apply(null,v(n)):null}function k(n){if(!y(n))return null;var t=v(n),r=t[0]+1+7*t[2];return n[2]*r+M(r,t[1])}function D(n){return G(n)||k(n)}function S(n,t,r){return function(e){return function(u){if(n(u))return e(u);var i=t(u);return i?r(e(i)):null}}}function U(n,t){var r=A(t);if(!r)return null;var e=O(n)+O(t);if(j(t))return["tnlp",[e]];var u=w(n)+w(t);if("note"===r)return["tnlp",[e,u]];var i=I(n)+I(t)<0?-1:1;return["tnlp",[i*e,i*u],i]}function $(n,t){if(1===arguments.length)return function(t){return $(n,t)};var r=E(n),e=E(t),u=y(r)?U(r,e):y(e)?U(e,r):null;return n===r&&t===e?u:D(u)}function B(n,t){return arguments.length>1?B(n)(t):function(t){return $(n,d(t,0,1))}}function N(n,t){if(!n||!t||n[1].length!==t[1].length)return null;var r=O(t)-O(n);if(j(n))return d(r,-Math.floor(7*r/12),1);var e=w(t)-w(n),u=I(t)-I(n)<0?-1:1;return d(u*r,u*e,u)}function V(n,t){if(1===arguments.length)return function(t){return V(n,t)};var r=E(n),e=E(t),u=N(r,e);return n===r&&t===e?u:k(u)}function T(n,t){var r=N(E(n),E(t));return r?I(r):null}function R(n){return n||0===n}function K(n,t){return arguments.length>1?K(n)(t):function(t){return rr(t).map(n)}}function L(n){return rr(n).filter(R)}function H(n,t){return arguments.length>1?H(n)(t):function(t){return rr(t).filter(n)}}function J(n){if(!n)return-1/0;var t=7*O(n);return t+12*(w(n)||-Math.floor(t/12)-100)}function Q(n,t){return J(n)-J(t)}function W(n,t){return-Q(n,t)}function X(n,t){var r=1===arguments.length||!0===t?Q:!1===t?W:"function"==typeof t?t:Q;return n=Array.isArray(n)?n.slice():rr(n),un(function(n){return n.sort(r).filter(R)},n)}function Y(n){return $(d(0,n,1))}function Z(n,t){var r=rr(t),e=r.length,u=(n%e+e)%e;return r.slice(u,e).concat(r.slice(0,u))}function nn(n,t){if(1===arguments.length)return function(t){return nn(n,t)};var r=rr(t);return rr(n).map(function(n){return r[n-1]||null})}function tn(n){return 0===(n=rr(n)).length?[[]]:tn(n.slice(1)).reduce(function(t,r){return t.concat(n.map(function(t,e){var u=r.slice();return u.splice(e,0,n[0]),u}))},[])}function rn(n){return D(n)||n}function en(n){return h(n)?D(n):tr(n)?n.map(rn):n}function un(n,t){if(1===arguments.length)return function(t){return un(n,t)};var r=rr(t).map(E);return en(n(r))}function on(n){var t=rr(n);return t.length?L(t.map(V(t[0]))):t}function cn(n){var t=[];n=rr(n);for(var r=1;r1?mn(n)(t):function(t){return L(K($(t||"P1"),n))}}function an(n,t){return Array(t+1).join(n)}function Pn(n){return"number"==typeof n}function Mn(n,t){return Math.pow(2,(n-69)/12)*(t||440)}function ln(n,t,r){if("string"!=typeof n)return null;var e=or.exec(n);if(!e||!t&&e[4])return null;var u={letter:e[1].toUpperCase(),acc:e[2].replace(/x/g,"##")};u.pc=u.letter+u.acc,u.step=(u.letter.charCodeAt(0)+3)%7,u.alt="b"===u.acc[0]?-u.acc.length:u.acc.length;var i=cr[u.step]+u.alt;return u.chroma=i<0?12+i:i%12,e[3]&&(u.oct=+e[3],u.midi=i+12*(u.oct+1),u.freq=Mn(u.midi,r)),t&&(u.tonicOf=e[4]),u}function fn(n){return Pn(n)?n<0?an("b",-n):an("#",n):""}function sn(n){return Pn(n)?""+n:""}function pn(n,t,r){return null===n||void 0===n?null:n.step?pn(n.step,n.alt,n.oct):n<0||n>6?null:mr.charAt(n)+fn(t)+sn(r)}function dn(n){return"number"==typeof n}function hn(n){return"string"==typeof n}function bn(n){return void 0!==n}function vn(n,t){return Math.pow(2,(n-69)/12)*(t||440)}function An(n,t,r){if("string"!=typeof n)return null;var e=ar.exec(n);if(!e||!t&&e[4])return null;var u={letter:e[1].toUpperCase(),acc:e[2].replace(/x/g,"##")};u.pc=u.letter+u.acc,u.step=(u.letter.charCodeAt(0)+3)%7,u.alt="b"===u.acc[0]?-u.acc.length:u.acc.length;var i=Pr[u.step]+u.alt;return u.chroma=i<0?12+i:i%12,e[3]&&(u.oct=+e[3],u.midi=i+12*(u.oct+1),u.freq=vn(u.midi,r)),t&&(u.tonicOf=e[4]),u}function gn(n){if((dn(n)||hn(n))&&n>=0&&n<128)return+n;var t=An(n);return t&&bn(t.midi)?t.midi:null}function yn(n){return Array.isArray(n)&&2===n.length?7*n[0]+12*n[1]+12:gn(n)}function jn(n,t){return!0===n||!1===n?function(t){return jn(t,n)}:(n=Math.round(n),(!0===t?lr:Mr)[n%12]+(Math.floor(n/12)-1))}function xn(n,t){return n=!(!n&&0!==n)&&Math.pow(10,n),function(r){return null===(r=t(r))?null:n?Math.round(r*n)/n:r}}function On(n,t,r){return arguments.length>2?On(n,t)(r):xn(t,function(t){var r=yn(t);return r?Math.pow(2,(r-69)/12)*n:null})}function wn(n,t,r){return arguments.length>2?wn(n,t)(r):xn(t,function(t){return 12*(Math.log(t)-Math.log(n))/Math.log(2)+69})}function In(n){return"string"!=typeof n?null:hr[n]||(hr[n]=ln(n))}function Cn(n){var t=q(n);return t?G(t):null}function zn(n){console.warn("note.props() is deprecated. Use: note.step(), note.alt() or note.oct()");var t=q(n);if(!t)return null;var r=v(t);return{step:r[0],alt:r[1],oct:r[2]}}function _n(n){return function(t){var r=zn(t);return r?r[n]:null}}function qn(n){var t=q(n);return t?O(t):null}function Fn(n){var t=q(n);return t?G([t[0],[O(t)]]):null}function En(n){var t=[];return t.push($(Or,n)),null===t[0]?null:(t.push(n),t.push($(xr,n)),t)}function Gn(n){return Ir[(n-1)%7]}function kn(n){return-1===n?"-":""}function Dn(n,t){return n+7*t}function Sn(n,t,r,e){return kn(e)+Dn(n,r)+$n(n,t)}function Un(n,t){return Array(Math.abs(t)+1).join(n)}function $n(n,t){var r="number"==typeof n?Gn(Math.abs(n)):n;return 0===t?"M"===r?"M":"P":-1===t&&"M"===r?"m":t>0?Un("A",t):t<0?Un("d","P"===r?t:t+1):null}function Bn(n){var t=F(n);if(!t)return null;var r=v(t);return{num:r[0]+1+7*r[2],alt:r[1],dir:t[2]}}function Nn(n){if(!n||n.num<1)return null;var t=Math.floor(n.num/8);return Sn(n.num-7*t,n.alt||0,t,n.dir)}function Vn(n){var t=F(n),r=t?C(t):Math.round(n);return isNaN(r)?null:_r[Math.abs(r)%12]}function Tn(n){return parseInt(Kn(n),2)}function Rn(n){return(n=E(n))?C(n):null}function Kn(n){if(Hn(n))return n;var t=[0,0,0,0,0,0,0,0,0,0,0,0];return K(Rn,n).forEach(function(n){t[n]=1}),t.join("")}function Ln(n,t){t=!1!==t;var r=Kn(n).split("");return L(r.map(function(n,e){var u=Z(e,r);return t&&"0"===u[0]?null:u.join("")}))}function Hn(n){return kr.test(n)}function Jn(n){return L(Kn(n).split("").map(function(n,t){return"1"===n?Dr[t]:null}))}function Qn(n,t){return console.warn("pcset.fromChroma is deprecated. Use pcset.intervals().map(...)"),1===arguments.length?function(t){return Qn(n,t)}:(t||(t="P1"),Jn(n).map($(t)))}function Wn(n,t){return 1===arguments.length?function(t){return Wn(n,t)}:Kn(n)===Kn(t)}function Xn(n,t){return 1===arguments.length?function(t){return Xn(n,t)}:((t=Tn(t))&Tn(n))===t}function Yn(n,t){return 1===arguments.length?function(t){return Yn(n,t)}:((t=Tn(t))|Tn(n))===t}function Zn(n,t){return arguments.length>1?Zn(n)(t):(n=Kn(n),function(t){return"1"===n[Rn(t)]})}function nt(n,t){return 1===arguments.length?function(t){return nt(n,t)}:rr(t).filter(Zn(n))}function tt(n){return"number"==typeof n}function rt(n){return tt(n)?n:yn(n)}function et(n,t){for(var r=[];t--;r[t]=t+n);return r}function ut(n,t){for(var r=[];t--;r[t]=n-t);return r}function it(n,t){return null===n||null===t?[]:n6)}function Pt(n){return/^b+$/.test(n)}function Mt(n){return/^#+$/.test(n)}function lt(n,t){return Array(t+1).join(n)}function ft(n){return n?n<0?lt("b",-n):lt("#",n):""}function st(n,t){return n?n+" "+t:t}function pt(n){return st(B("C",n),"major")}function dt(n){return Nr[Br.indexOf(n)]}function ht(n){return n=n.trim().toLowerCase(),-1===Br.indexOf(n)?null:n}function bt(n){if("string"!=typeof n)return null;var t,r=n.indexOf(" ");if(-1===r){var e=Fn(n);t=e?{tonic:e,mode:"major"}:{tonic:!1,mode:ht(n)}}else t={tonic:Fn(n.slice(0,r)),mode:ht(n.slice(r+1))};return t.mode?t:null}function vt(n,t){return 1===arguments.length?function(t){return vt(n,t)}:!(n=bt(n))||n.tonic?null:(t=bt(t))&&t.tonic?st(B(t.tonic,dt(n.mode)-dt(t.mode)),n.mode):null}function At(n){var t=bt(n);if(!t||!t.tonic)return null;var r=dt(t.mode);return qn(t.tonic)-r}function gt(n){return ft(At(n))}function yt(n){return n}function jt(n,t){t=t||yt;var r={},e=Object.keys(n),u=[];return e.forEach(function(e){var i=t(n[e][0]);r[e]=i,n[e][1]&&n[e][1].forEach(function(n){r[n]=i,u.push(n)})}),{get:function(n){return r[n]},keys:function(n,t){var i=n?e.concat(u):e.slice();return"function"!=typeof t?i:i.filter(function(n){return t(n,r[n])})}}}function xt(n,t){var r="string"==typeof t,e="function"==typeof t,u=n.keys(!1).reduce(function(t,r){return t[Kn(n.get(r))]=r,t},{});return function(n){return L(Ln(n=X(K(Fn,n))).map(function(i,o){var c=u[i];if(!c)return null;var m=n[o];return r?m+t+c:e?t(c,m):[c,m]}))}}function Ot(n,t){if(1===arguments.length)return function(t){return Ot(n,t)};var r=Kr.get(n);return r?mn(r,t):null}function wt(n){return Ot(It(n).type,!1)||[]}function It(n){if("string"!=typeof n)return null;var t=n.indexOf(" "),r=Cn(n.substring(0,t))||!1;return{tonic:r,type:r?n.substring(t+1):n}}function Ct(){return Qr}function zt(n,t){if(1===arguments.length)return function(t){return zt(n,t)};var r=Wr.get(n);return r?mn(r,t):null}function _t(n){var t=kt(n),r=Wr.get(t.type);return r?mn(r,t.tonic):L(K(Cn,n))}function qt(n){var t=kt(n);return Wr.get(t.type)||[]}function Ft(n,t){if(1===arguments.length)return function(t){return Ft(n,t)};var r=Et(t);return r?Z(n,r):[]}function Et(n){for(var t=tn(_t(n).map(Fn)),r=0;rt.length?t:n:t},null)}}),Ir="PMMPPMM",Cr=[1,2,2,3,3,4,5,5,6,6,7,7],zr="P m M m M P d P m M m M".split(" "),_r=[0,1,2,3,4,5,6,5,4,3,2,1],qr="PMMPPMM",Fr=Wt(function(n){var t=v(n);return b((7-t[0])%7,"P"===qr[t[0]]?-t[1]:-(t[1]+1),t[2],x(n))}),Er=Wt(function(n){var t=v(n);return 0===t[0]&&1===t[2]||(t[2]=0),b(t[0],t[1],t[2],x(n))}),Gr=Object.freeze({toInterval:function(n){var t=F(n);return t?k(t):null},num:function(n){var t=Bn(n);return t?t.num:null},value:function(n){var t=Bn(n);return t?t.num*t.dir:null},props:Bn,fromProps:Nn,semitones:function(n){var t=F(n);return t?I(t):null},fromSemitones:function(n){var t=n<0?-1:1,r=Math.abs(n),e=r%12,u=Math.floor(r/12);return t*(Cr[e]+7*u)+zr[e]},ic:Vn,type:function(n){var t=F(n);return t?qr[v(t)[0]]:null},invert:Fr,simplify:Er}),kr=/^[01]{12}$/,Dr="1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M".split(" "),Sr=Object.freeze({chroma:Kn,notes:function(n){console.warn("pcset.notes deprecated. Use collection.pcset");var t=K(Fn,n);if(!t.length)return t;var r=t[0];return Qn(Z(Rn(r),Kn(t).split("")).join(""),r)},modes:Ln,chromaModes:function(n,t){return console.warn("pcset.chromaModes deprecated. Renamed to pcset.modes"),Ln(n,t)},isChroma:Hn,intervals:Jn,fromChroma:Qn,isEqual:Wn,equal:function(n,t){return console.warn("pcset.equal is deprecated. Use pcset.isEqual"),Wn(n,t)},isSubset:Xn,subset:function(n,t){return console.warn("pcset.subset is deprecated. Use pcset.isSubset"),Xn(n,t)},isSuperset:Yn,superset:function(n,t){return console.warn("pcset.superset is deprecated. Use pcset.isSuperset"),Yn(n,t)},includes:Zn,filter:nt}),Ur=Object.freeze({numeric:ot,chromatic:ct,fifths:function(n,t){return ot(t).map(B(n))},pitchSet:mt}),$r=Object.freeze({toStep:function(n){var t="CDEFGAB".indexOf(n.toUpperCase());return t<0?null:t},isStep:at,toLetter:function(n){return at(n)?"CDEFGAB".charAt(n):null},areFlats:Pt,areSharps:Mt,toAlt:function(n){return""===n?0:Pt(n)?-n.length:Mt(n)?n.length:null},toAcc:ft}),Br=["ionian","dorian","phrygian","lydian","mixolydian","aeolian","locrian","major","minor"],Nr=[0,2,4,-1,1,3,5,0,3],Vr=[0,1,2,3,4,5,6,0,5].map(function(n){return on(Z(n,["C","D","E","F","G","A","B"]))}),Tr=gt,Rr=Object.freeze({props:bt,isKeyName:function(n){return null!==bt(n)},tonic:function(n){return(bt(n)||n||{}).tonic||null},mode:function(n){return(bt(n)||n||{}).mode||null},relative:vt,alteredNotes:function(n){var t=At(n);return null===t?null:t<0?ot([-1,t]).map(B("F")):ot([1,t]).map(B("B"))},modes:function(n){return n?Br.slice():Br.slice(0,-2)},fromAlter:function(n){return"number"==typeof n?pt(n):null},fromAcc:function(n){return Mt(n)?pt(n.length):Pt(n)?pt(-n.length):null},scale:function(n){var t=bt(n);return t&&t.tonic?mn(Vr[Br.indexOf(t.mode)],t.tonic):null},alteration:At,signature:gt,accidentals:Tr}),Kr=jt({chromatic:["1P 2m 2M 3m 3M 4P 4A 5P 6m 6M 7m 7M"],lydian:["1P 2M 3M 4A 5P 6M 7M"],major:["1P 2M 3M 4P 5P 6M 7M",["ionian"]],mixolydian:["1P 2M 3M 4P 5P 6M 7m",["dominant"]],dorian:["1P 2M 3m 4P 5P 6M 7m"],aeolian:["1P 2M 3m 4P 5P 6m 7m",["minor"]],phrygian:["1P 2m 3m 4P 5P 6m 7m"],locrian:["1P 2m 3m 4P 5d 6m 7m"],altered:["1P 2m 3m 3M 5d 6m 7m",["super locrian","diminished whole tone","pomeroy"]],iwato:["1P 2m 4P 5d 7m"],hirajoshi:["1P 2M 3m 5P 6m"],kumoijoshi:["1P 2m 4P 5P 6m"],pelog:["1P 2m 3m 5P 6m"],prometheus:["1P 2M 3M 4A 6M 7m"],ritusen:["1P 2M 4P 5P 6M"],scriabin:["1P 2m 3M 5P 6M"],piongio:["1P 2M 4P 5P 6M 7m"],augmented:["1P 2A 3M 5P 5A 7M"],neopolitan:["1P 2m 3m 4P 5P 6m 7M"],diminished:["1P 2M 3m 4P 5d 6m 6M 7M"],egyptian:["1P 2M 4P 5P 7m"],oriental:["1P 2m 3M 4P 5d 6M 7m"],spanish:["1P 2m 3M 4P 5P 6m 7m",["phrygian major"]],flamenco:["1P 2m 3m 3M 4A 5P 7m"],balinese:["1P 2m 3m 4P 5P 6m 7M"],persian:["1P 2m 3M 4P 5d 6m 7M"],bebop:["1P 2M 3M 4P 5P 6M 7m 7M"],enigmatic:["1P 2m 3M 5d 6m 7m 7M"],ichikosucho:["1P 2M 3M 4P 5d 5P 6M 7M"],"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"],"locrian major":["1P 2M 3M 4P 5d 6m 7m",["arabian"]],"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"],"vietnamese 1":["1P 3m 4P 5P 6m"],"vietnamese 2":["1P 3m 4P 5P 7m"],"prometheus neopolitan":["1P 2m 3M 4A 6M 7m"],"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 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"],"harmonic minor":["1P 2M 3m 4P 5P 6m 7M"],"lydian minor":["1P 2M 3M 4A 5P 6m 7m"],"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"]],"hungarian minor":["1P 2M 3m 4A 5P 6m 7M"],"hungarian major":["1P 2A 3M 4A 5P 6M 7m"],"spanish heptatonic":["1P 2m 3m 3M 4P 5P 6m 7m"],"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"],"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"],"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"],"six tone symmetric":["1P 2m 3M 4P 5A 6M"]},function(n){return n.split(" ")}),Lr=Kr.keys,Hr=xt(Kr," "),Jr=Object.freeze({get:Ot,names:Lr,notes:function(n){var t=It(n);return(t.tonic?Ot(t.type,Fn(t.tonic)):null)||L(K(Fn,n).map(function(n,t,r){return r.indexOf(n)0},parse:It,detect:Hr}),Qr=/^([a-gA-G])(#{1,}|b{1,}|x{1,}|)(-?\d*)\s*(.*)\s*$/,Wr=jt({M:["1P 3M 5P",["Major",""]],M13:["1P 3M 5P 7M 9M 13M",["maj13","Maj13"]],M6:["1P 3M 5P 13M",["6"]],M69:["1P 3M 5P 6M 9M",["69"]],M7add13:["1P 3M 5P 6M 7M 9M"],M7b5:["1P 3M 5d 7M"],M7b6:["1P 3M 6m 7M"],M7b9:["1P 3M 5P 7M 9m"],M7sus4:["1P 4P 5P 7M"],M9:["1P 3M 5P 7M 9M",["maj9","Maj9"]],M9b5:["1P 3M 5d 7M 9M"],M9sus4:["1P 4P 5P 7M 9M"],Madd9:["1P 3M 5P 9M",["2","add9","add2"]],Maj7:["1P 3M 5P 7M",["maj7","M7"]],Mb5:["1P 3M 5d"],Mb6:["1P 3M 13m"],Msus2:["1P 2M 5P",["add9no3","sus2"]],Msus4:["1P 4P 5P",["sus","sus4"]],addb9:["1P 3M 5P 9m"],m:["1P 3m 5P",["minor"]],m11:["1P 3m 5P 7m 9M 11P",["_11"]],m11b5:["1P 3m 7m 12d 2M 4P",["h11","_11b5"]],m13:["1P 3m 5P 7m 9M 11P 13M",["_13"]],m6:["1P 3m 4P 5P 13M",["_6"]],m69:["1P 3m 5P 6M 9M",["_69"]],m7:["1P 3m 5P 7m",["minor7","_","_7"]],m7add11:["1P 3m 5P 7m 11P",["m7add4"]],m7b5:["1P 3m 5d 7m",["half-diminished","h7","_7b5"]],m9:["1P 3m 5P 7m 9M",["_9"]],m9b5:["1P 3m 7m 12d 2M",["h9","-9b5"]],mMaj7:["1P 3m 5P 7M",["mM7","_M7"]],mMaj7b6:["1P 3m 5P 6m 7M",["mM7b6"]],mM9:["1P 3m 5P 7M 9M",["mMaj9","-M9"]],mM9b6:["1P 3m 5P 6m 7M 9M",["mMaj9b6"]],mb6M7:["1P 3m 6m 7M"],mb6b9:["1P 3m 6m 9m"],o:["1P 3m 5d",["mb5","dim"]],o7:["1P 3m 5d 13M",["diminished","m6b5","dim7"]],o7M7:["1P 3m 5d 6M 7M"],oM7:["1P 3m 5d 7M"],sus24:["1P 2M 4P 5P",["sus4add9"]],madd4:["1P 3m 4P 5P"],madd9:["1P 3m 5P 9M"],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#5":["1P 3M 5A",["augmented","maj#5","Maj#5","+","aug"]],"M#5add9":["1P 3M 5A 9M",["+add9"]],"M13#11":["1P 3M 5P 7M 9M 11A 13M",["maj13#11","Maj13#11","M13+4","M13#4"]],"M6#11":["1P 3M 5P 6M 11A",["M6b5","6#11","6b5"]],"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"],"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"],"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#5":["1P 3m 5A",["m+","mb6"]],"m11A 5":["1P 3m 6m 7m 9M 11P"],"m7#5":["1P 3m 6m 7m"],"m9#5":["1P 3m 6m 7m 9M"],"+add#9":["1P 3M 5A 9A"]},function(n){return n.split(" ")}),Xr=Wr.keys,Yr=xt(Wr,""),Zr=Object.freeze({names:Xr,get:zt,notes:_t,intervals:qt,isKnownChord:function(n){return qt(n).length>0},detect:Yr,position:function(n){var t=K(Fn,n),r=Et(t);return r?r.indexOf(t[0]):null},inversion:Ft,parse:kt}),ne=["I","II","III","IV","V","VI","VII"],te=/^\s*(b|bb|#|##|)(IV|III|II|I|VII|VI|V|iv|iii|ii|i|vii|vi|v)\s*(.*)\s*$/,re={i:0,ii:1,iii:2,iv:3,v:4,vi:5,vii:6},ee=Object.freeze({abstract:function(n,t){t=Fn(t);var r=L((n=K(kt,n)).map(function(n){return n.tonic}));return r.length!==n.length?null:r.map(function(r,e){var u=Bn(V(t,r));return Dt(u.num-1,u.alt,n[e].type)})},buildRoman:Dt,concrete:function(n,t){return K(function(n){var r=St(n);return r?$(r.root,t)+r.type:null},n)},romanRegex:function(){return te},parseRomanChord:St}),ue=Object.freeze({density:function(n){var t,r,e,u=L(K(q,n)),i=u.length,o=[0,0,0,0,0,0];for(t=0;t0&&(o[5-e]=o[5-e]+1);return o}}),ie=Object.freeze({notes:function(n){return X(n).filter(function(n,t,r){return 0===t||n!==r[t-1]})}}),oe=Object.assign,ce=oe({},ur,Zt,ir,nr);return ce.pitch=Yt,ce.notation=$r,ce.note=wr,ce.ivl=Gr,ce.midi=fr,ce.freq=dr,ce.range=Ur,ce.key=Rr,ce.progression=ee,ce.sonority=ue,ce.pitchset=ie,ce.pcset=Sr,ce.scale=function(n){return ce.scale.notes(n)},oe(ce.scale,Jr),ce.chord=function(n){return ce.chord.notes(n)},oe(ce.chord,Zr),"undefined"!=typeof window&&(window.Tonal=ce),ce}); +//# sourceMappingURL=tonal.min.js.map diff --git a/dist/tonal.min.js.map b/dist/tonal.min.js.map new file mode 100644 index 0000000..f68961a --- /dev/null +++ b/dist/tonal.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tonal.min.js","sources":["../packages/core/pitch/node_modules/note-parser/index.js","../packages/core/pitch/node_modules/interval-notation/index.js","../packages/core/encoding/index.js","../packages/core/pitch/index.js","../packages/core/transpose/index.js","../packages/core/distance/index.js","../packages/core/array/index.js","../packages/core/harmonizer/index.js","../packages/tonal/note/node_modules/note-parser/index.js","../packages/core/midi/node_modules/note-parser/index.js","../packages/core/midi/index.js","../packages/core/freq/index.js","../packages/tonal/note/index.js","../packages/tonal/interval/node_modules/interval-notation/index.js","../packages/tonal/interval/index.js","../packages/core/pcset/index.js","../packages/core/range/index.js","../packages/core/notation/index.js","../packages/extensions/key/index.js","../packages/core/dictionary/index.js","../packages/tonal/scale/index.js","../packages/tonal/chord/node_modules/note-parser/index.js","../packages/tonal/chord/index.js","../packages/extensions/progression/index.js","../packages/incubator/sonority/index.js","../packages/incubator/pitchset/index.js","../packages/core/tonal/index.js"],"sourcesContent":["'use strict'\n\n// util\nfunction fillStr (s, num) { return Array(num + 1).join(s) }\nfunction isNum (x) { return typeof x === 'number' }\nfunction isStr (x) { return typeof x === 'string' }\nfunction isDef (x) { return typeof x !== 'undefined' }\nfunction midiToFreq (midi, tuning) {\n return Math.pow(2, (midi - 69) / 12) * (tuning || 440)\n}\n\nvar REGEX = /^([a-gA-G])(#{1,}|b{1,}|x{1,}|)(-?\\d*)\\s*(.*)\\s*$/\n/**\n * A regex for matching note strings in scientific notation.\n *\n * @name regex\n * @function\n * @return {RegExp} the regexp used to parse the note name\n *\n * The note string should have the form `letter[accidentals][octave][element]`\n * where:\n *\n * - letter: (Required) is a letter from A to G either upper or lower case\n * - accidentals: (Optional) can be one or more `b` (flats), `#` (sharps) or `x` (double sharps).\n * They can NOT be mixed.\n * - octave: (Optional) a positive or negative integer\n * - element: (Optional) additionally anything after the duration is considered to\n * be the element name (for example: 'C2 dorian')\n *\n * The executed regex contains (by array index):\n *\n * - 0: the complete string\n * - 1: the note letter\n * - 2: the optional accidentals\n * - 3: the optional octave\n * - 4: the rest of the string (trimmed)\n *\n * @example\n * var parser = require('note-parser')\n * parser.regex.exec('c#4')\n * // => ['c#4', 'c', '#', '4', '']\n * parser.regex.exec('c#4 major')\n * // => ['c#4major', 'c', '#', '4', 'major']\n * parser.regex().exec('CMaj7')\n * // => ['CMaj7', 'C', '', '', 'Maj7']\n */\nexport function regex () { return REGEX }\n\nvar SEMITONES = [0, 2, 4, 5, 7, 9, 11]\n/**\n * Parse a note name in scientific notation an return it's components,\n * and some numeric properties including midi number and frequency.\n *\n * @name parse\n * @function\n * @param {String} note - the note string to be parsed\n * @param {Boolean} isTonic - true the strings it's supposed to contain a note number\n * and some category (for example an scale: 'C# major'). It's false by default,\n * but when true, en extra tonicOf property is returned with the category ('major')\n * @param {Float} tunning - The frequency of A4 note to calculate frequencies.\n * By default it 440.\n * @return {Object} the parsed note name or null if not a valid note\n *\n * The parsed note name object will ALWAYS contains:\n * - letter: the uppercase letter of the note\n * - acc: the accidentals of the note (only sharps or flats)\n * - pc: the pitch class (letter + acc)\n * - step: s a numeric representation of the letter. It's an integer from 0 to 6\n * where 0 = C, 1 = D ... 6 = B\n * - alt: a numeric representation of the accidentals. 0 means no alteration,\n * positive numbers are for sharps and negative for flats\n * - chroma: a numeric representation of the pitch class. It's like midi for\n * pitch classes. 0 = C, 1 = C#, 2 = D ... 11 = B. Can be used to find enharmonics\n * since, for example, chroma of 'Cb' and 'B' are both 11\n *\n * If the note has octave, the parser object will contain:\n * - oct: the octave number (as integer)\n * - midi: the midi number\n * - freq: the frequency (using tuning parameter as base)\n *\n * If the parameter `isTonic` is set to true, the parsed object will contain:\n * - tonicOf: the rest of the string that follows note name (left and right trimmed)\n *\n * @example\n * var parse = require('note-parser').parse\n * parse('Cb4')\n * // => { letter: 'C', acc: 'b', pc: 'Cb', step: 0, alt: -1, chroma: -1,\n * oct: 4, midi: 59, freq: 246.94165062806206 }\n * // if no octave, no midi, no freq\n * parse('fx')\n * // => { letter: 'F', acc: '##', pc: 'F##', step: 3, alt: 2, chroma: 7 })\n */\nexport function parse (str, isTonic, tuning) {\n if (typeof str !== 'string') return null\n var m = REGEX.exec(str)\n if (!m || (!isTonic && m[4])) return null\n\n var p = { letter: m[1].toUpperCase(), acc: m[2].replace(/x/g, '##') }\n p.pc = p.letter + p.acc\n p.step = (p.letter.charCodeAt(0) + 3) % 7\n p.alt = p.acc[0] === 'b' ? -p.acc.length : p.acc.length\n var pos = SEMITONES[p.step] + p.alt\n p.chroma = pos < 0 ? 12 + pos : pos % 12\n if (m[3]) { // has octave\n p.oct = +m[3]\n p.midi = pos + 12 * (p.oct + 1)\n p.freq = midiToFreq(p.midi, tuning)\n }\n if (isTonic) p.tonicOf = m[4]\n return p\n}\n\nvar LETTERS = 'CDEFGAB'\nfunction accStr (n) { return !isNum(n) ? '' : n < 0 ? fillStr('b', -n) : fillStr('#', n) }\nfunction octStr (n) { return !isNum(n) ? '' : '' + n }\n\n/**\n * Create a string from a parsed object or `step, alteration, octave` parameters\n * @param {Object} obj - the parsed data object\n * @return {String} a note string or null if not valid parameters\n * @since 1.2\n * @example\n * parser.build(parser.parse('cb2')) // => 'Cb2'\n *\n * @example\n * // it accepts (step, alteration, octave) parameters:\n * parser.build(3) // => 'F'\n * parser.build(3, -1) // => 'Fb'\n * parser.build(3, -1, 4) // => 'Fb4'\n */\nexport function build (s, a, o) {\n if (s === null || typeof s === 'undefined') return null\n if (s.step) return build(s.step, s.alt, s.oct)\n if (s < 0 || s > 6) return null\n return LETTERS.charAt(s) + accStr(a) + octStr(o)\n}\n\n/**\n * Get midi of a note\n *\n * @name midi\n * @function\n * @param {String|Integer} note - the note name or midi number\n * @return {Integer} the midi number of the note or null if not a valid note\n * or the note does NOT contains octave\n * @example\n * var parser = require('note-parser')\n * parser.midi('A4') // => 69\n * parser.midi('A') // => null\n * @example\n * // midi numbers are bypassed (even as strings)\n * parser.midi(60) // => 60\n * parser.midi('60') // => 60\n */\nexport function midi (note) {\n if ((isNum(note) || isStr(note)) && note >= 0 && note < 128) return +note\n var p = parse(note)\n return p && isDef(p.midi) ? p.midi : null\n}\n\n/**\n * Get freq of a note in hertzs (in a well tempered 440Hz A4)\n *\n * @name freq\n * @function\n * @param {String} note - the note name or note midi number\n * @param {String} tuning - (Optional) the A4 frequency (440 by default)\n * @return {Float} the freq of the number if hertzs or null if not valid note\n * @example\n * var parser = require('note-parser')\n * parser.freq('A4') // => 440\n * parser.freq('A') // => null\n * @example\n * // can change tuning (440 by default)\n * parser.freq('A4', 444) // => 444\n * parser.freq('A3', 444) // => 222\n * @example\n * // it accepts midi numbers (as numbers and as strings)\n * parser.freq(69) // => 440\n * parser.freq('69', 442) // => 442\n */\nexport function freq (note, tuning) {\n var m = midi(note)\n return m === null ? null : midiToFreq(m, tuning)\n}\n\nexport function letter (src) { return (parse(src) || {}).letter }\nexport function acc (src) { return (parse(src) || {}).acc }\nexport function pc (src) { return (parse(src) || {}).pc }\nexport function step (src) { return (parse(src) || {}).step }\nexport function alt (src) { return (parse(src) || {}).alt }\nexport function chroma (src) { return (parse(src) || {}).chroma }\nexport function oct (src) { return (parse(src) || {}).oct }\n","// shorthand tonal notation (with quality after number)\nvar IVL_TNL = '([-+]?)(\\\\d+)(d{1,4}|m|M|P|A{1,4})'\n// standard shorthand notation (with quality before number)\nvar IVL_STR = '(AA|A|P|M|m|d|dd)([-+]?)(\\\\d+)'\nvar COMPOSE = '(?:(' + IVL_TNL + ')|(' + IVL_STR + '))'\nvar IVL_REGEX = new RegExp('^' + COMPOSE + '$')\n\n/**\n * Parse a string with an interval in shorthand notation (https://en.wikipedia.org/wiki/Interval_(music)#Shorthand_notation)\n * and returns an object with interval properties.\n *\n * @param {String} str - the string with the interval\n * @param {Boolean} strict - (Optional) if its false, it doesn't check if the\n * interval is valid or not. For example, parse('P2') returns null\n * (because a perfect second is not a valid interval), but\n * parse('P2', false) it returns { num: 2, dir: 1, q: 'P'... }\n * @return {Object} an object properties or null if not valid interval string\n * The returned object contains:\n * - `num`: the interval number\n * - `q`: the interval quality string (M is major, m is minor, P is perfect...)\n * - `simple`: the simplified number (from 1 to 7)\n * - `dir`: the interval direction (1 ascending, -1 descending)\n * - `type`: the interval type (P is perfectable, M is majorable)\n * - `alt`: the alteration, a numeric representation of the quality\n * - `oct`: the number of octaves the interval spans. 0 for simple intervals.\n * - `size`: the size of the interval in semitones\n * @example\n * var parse = require('interval-notation').parse\n * parse('M3')\n * // => { num: 3, q: 'M', dir: 1, simple: 3,\n * // type: 'M', alt: 0, oct: 0, size: 4 }\n */\nexport function parse (str, strict) {\n if (typeof str !== 'string') return null\n var m = IVL_REGEX.exec(str)\n if (!m) return null\n var i = { num: +(m[3] || m[8]), q: m[4] || m[6] }\n i.dir = (m[2] || m[7]) === '-' ? -1 : 1\n var step = (i.num - 1) % 7\n i.simple = step + 1\n i.type = TYPES[step]\n i.alt = qToAlt(i.type, i.q)\n i.oct = Math.floor((i.num - 1) / 7)\n i.size = i.dir * (SIZES[step] + i.alt + 12 * i.oct)\n if (strict !== false) {\n if (i.type === 'M' && i.q === 'P') return null\n }\n return i\n}\nvar SIZES = [0, 2, 4, 5, 7, 9, 11]\n\nvar TYPES = 'PMMPPMM'\n/**\n * Get the type of interval. Can be perfectavle ('P') or majorable ('M')\n * @param {Integer} num - the interval number\n * @return {String} `P` if it's perfectable, `M` if it's majorable.\n */\nexport function type (num) {\n return TYPES[(num - 1) % 7]\n}\n\nfunction dirStr (dir) { return dir === -1 ? '-' : '' }\nfunction num (simple, oct) { return simple + 7 * oct }\n\n/**\n * Build a shorthand interval notation string from properties.\n *\n * @param {Integer} simple - the interval simple number (from 1 to 7)\n * @param {Integer} alt - the quality expressed in numbers. 0 means perfect\n * or major, depending of the interval number.\n * @param {Integer} oct - the number of octaves the interval spans.\n * 0 por simple intervals. Positive number.\n * @param {Integer} dir - the interval direction: 1 ascending, -1 descending.\n * @example\n * var interval = require('interval-notation')\n * interval.shorthand(3, 0, 0, 1) // => 'M3'\n * interval.shorthand(3, -1, 0, -1) // => 'm-3'\n * interval.shorthand(3, 1, 1, 1) // => 'A10'\n */\nexport function shorthand (simple, alt, oct, dir) {\n return altToQ(simple, alt) + dirStr(dir) + num(simple, oct)\n}\n/**\n * Build a special shorthand interval notation string from properties.\n * The special shorthand interval notation changes the order or the standard\n * shorthand notation so instead of 'M-3' it returns '-3M'.\n *\n * The standard shorthand notation has a string 'A4' (augmented four) that can't\n * be differenciate from 'A4' (the A note in 4th octave), so the purpose of this\n * notation is avoid collisions\n *\n * @param {Integer} simple - the interval simple number (from 1 to 7)\n * @param {Integer} alt - the quality expressed in numbers. 0 means perfect\n * or major, depending of the interval number.\n * @param {Integer} oct - the number of octaves the interval spans.\n * 0 por simple intervals. Positive number.\n * @param {Integer} dir - the interval direction: 1 ascending, -1 descending.\n * @example\n * var interval = require('interval-notation')\n * interval.build(3, 0, 0, 1) // => '3M'\n * interval.build(3, -1, 0, -1) // => '-3m'\n * interval.build(3, 1, 1, 1) // => '10A'\n */\nexport function build (simple, alt, oct, dir) {\n return dirStr(dir) + num(simple, oct) + altToQ(simple, alt)\n}\n\n/**\n * Get an alteration number from an interval quality string.\n * It accepts the standard `dmMPA` but also sharps and flats.\n *\n * @param {Integer|String} num - the interval number or a string representing\n * the interval type ('P' or 'M')\n * @param {String} quality - the quality string\n * @return {Integer} the interval alteration\n * @example\n * qToAlt('M', 'm') // => -1 (for majorables, 'm' is -1)\n * qToAlt('P', 'A') // => 1 (for perfectables, 'A' means 1)\n * qToAlt('M', 'P') // => null (majorables can't be perfect)\n */\nexport function qToAlt (num, q) {\n var t = typeof num === 'number' ? type(num) : num\n if (q === 'M' && t === 'M') return 0\n if (q === 'P' && t === 'P') return 0\n if (q === 'm' && t === 'M') return -1\n if (/^A+$/.test(q)) return q.length\n if (/^d+$/.test(q)) return t === 'P' ? -q.length : -q.length - 1\n return null\n}\n\nfunction fillStr (s, n) { return Array(Math.abs(n) + 1).join(s) }\n/**\n * Get interval quality from interval type and alteration\n *\n * @function\n * @param {Integer|String} num - the interval number of the the interval\n * type ('M' for majorables, 'P' for perfectables)\n * @param {Integer} alt - the interval alteration\n * @return {String} the quality string\n * @example\n * altToQ('M', 0) // => 'M'\n */\nexport function altToQ (num, alt) {\n var t = typeof num === 'number' ? type(Math.abs(num)) : num\n if (alt === 0) return t === 'M' ? 'M' : 'P'\n else if (alt === -1 && t === 'M') return 'm'\n else if (alt > 0) return fillStr('A', alt)\n else if (alt < 0) return fillStr('d', t === 'P' ? alt : alt + 1)\n else return null\n}\n\n","/**\n * Functions to encoding and decoding pitches into fifths/octaves notation.\n *\n * This functions are very low level and it's probably you wont need them.\n * @private\n * @module encoding\n */\n\nfunction isNum(n) {\n return typeof n === \"number\";\n}\n\n// Map from letter step to number of fifths starting from 'C':\n// { C: 0, D: 2, E: 4, F: -1, G: 1, A: 3, B: 5 }\nvar FIFTHS = [0, 2, 4, -1, 1, 3, 5];\n// Given a number of fifths, return the octaves they span\nfunction fOcts(f) {\n return Math.floor(f * 7 / 12);\n}\n// Get the number of octaves it span each step\nvar FIFTH_OCTS = FIFTHS.map(fOcts);\n\n/**\n * Given a note's step, alteration and octave returns the fiths/octave\n * note encoding.\n * @param {number} step - the step number (0 = C, 1 = D, ...)\n * @param {number} alteration - the note alteration (..., -1 = 'b', 0 = '', 1 = '#', ...)\n * @param {number} octave - the note octave\n * @return {Array} the [fifths, octave] representation of that note\n */\nexport function encode(step, alt, oct) {\n var f = FIFTHS[step] + 7 * alt;\n if (!isNum(oct)) return [f];\n var o = oct - FIFTH_OCTS[step] - 4 * alt;\n return [f, o];\n}\n\n// Return the number of fifths as if it were unaltered\nfunction unaltered(f) {\n var i = (f + 1) % 7;\n return i < 0 ? 7 + i : i;\n}\n\n// We need to get the steps from fifths\n// Fifths for CDEFGAB are [ 0, 2, 4, -1, 1, 3, 5 ]\n// We add 1 to fifths to avoid negative numbers, so:\n// for ['F', 'C', 'G', 'D', 'A', 'E', 'B'] we have:\nvar STEPS = [3, 0, 4, 1, 5, 2, 6];\n\n/**\n * Decode a encoded pitch\n * @param {Number} fifths - the number of fifths\n * @param {Number} octs - the number of octaves to compensate the fifhts\n * @return {Array} in the form [step, alt, oct]\n */\nexport function decode(f, o) {\n var step = STEPS[unaltered(f)];\n var alt = Math.floor((f + 1) / 7);\n if (!isNum(o)) return [step, alt];\n var oct = o + 4 * alt + FIFTH_OCTS[step];\n return [step, alt, oct];\n}\n","/**\n * Functions to deal with pitches (either notes or intervals).\n *\n * This functions are very low level and more developer friendly of this functions\n * are exposed in the note and interval packages. It's unlikely you need them.\n * That's why __this module is NOT exported in the tonal package__.\n *\n * @private\n * @module pitch\n */\nimport { parse as noteParse, build as noteStr } from \"note-parser\";\nimport { parse as ivlParse, altToQ } from \"interval-notation\";\nimport { encode as enc, decode as dec } from \"tonal-encoding\";\n\n/**\n * Create a pitch\n * @param {Integer} fifths - the number of fifths from C or from P1\n * @param {Integer} focts - the number of encoded octaves\n * @param {Integer} dir - (Optional) Only required for intervals. Can be 1 or -1\n * @return {Pitch}\n */\nexport function pitch(fifths, focts, dir) {\n return dir ? [\"tnlp\", [fifths, focts], dir] : [\"tnlp\", [fifths, focts]];\n}\n/**\n * Test if an object is a pitch\n * @param {Pitch}\n * @return {Boolean}\n */\nexport function isPitch(p) {\n return Array.isArray(p) && p[0] === \"tnlp\";\n}\n/**\n * Encode a pitch\n * @param {Integer} step\n * @param {Integer} alt\n * @param {Integer} oct\n * @param {Integer} dir - (Optional)\n */\nexport function encode(s, a, o, dir) {\n return dir ? [\"tnlp\", enc(s, a, o), dir] : [\"tnlp\", enc(s, a, o)];\n}\n\n/**\n * Decode a pitch\n * @param {Pitch} the pitch\n * @return {Array} An array with [step, alt, oct]\n */\nexport function decode(p) {\n return dec.apply(null, p[1]);\n}\n\n/**\n * Get pitch type\n * @param {Pitch}\n * @return {String} 'ivl' or 'note' or null if not a pitch\n */\nexport function pType(p) {\n return !isPitch(p) ? null : p[2] ? \"ivl\" : \"note\";\n}\n/**\n * Test if is a pitch note (with or without octave)\n * @param {Pitch}\n * @return {Boolean}\n */\nexport function isNotePitch(p) {\n return pType(p) === \"note\";\n}\n/**\n * Test if is an interval\n * @param {Pitch}\n * @return {Boolean}\n */\nexport function isIvlPitch(p) {\n return pType(p) === \"ivl\";\n}\n/**\n * Test if is a pitch class (a pitch note without octave)\n * @param {Pitch}\n * @return {Boolean}\n */\nexport function isPC(p) {\n return isPitch(p) && p[1].length === 1;\n}\n\n/**\n * Get direction of a pitch (even for notes)\n * @param {Pitch}\n * @return {Integer} 1 or -1\n */\nexport function dir(p) {\n return p[2] === -1 ? -1 : 1;\n}\n\n/**\n * Get encoded fifths from pitch.\n * @param {Pitch}\n * @return {Integer}\n */\nexport function fifths(p) {\n return p[2] === -1 ? -p[1][0] : p[1][0];\n}\n/**\n * Get encoded octaves from pitch.\n * @param {Pitch}\n * @return {Integer}\n */\nexport function focts(p) {\n return p[2] === -1 ? -p[1][1] : p[1][1];\n}\n/**\n * Get height of a pitch.\n * @param {Pitch}\n * @return {Integer}\n */\nexport function height(p) {\n return fifths(p) * 7 + focts(p) * 12;\n}\n\n/**\n * Get chroma of a pitch. The chroma is a number between 0 and 11 to represent\n * the position of a pitch inside an octave. Is the numeric equivlent of a\n * pitch class.\n *\n * @param {Pitch}\n * @return {Integer}\n */\nexport function chr(p) {\n var f = fifths(p);\n return 7 * f - 12 * Math.floor(f * 7 / 12);\n}\n\n// memoize parsers\nfunction memoize(fn) {\n var cache = {};\n return function(str) {\n if (typeof str !== \"string\") return null;\n return cache[str] || (cache[str] = fn(str));\n };\n}\n\n/**\n * Parse a note\n * @function\n * @param {String} str\n * @return {Pitch} the pitch or null if not valid note string\n */\nexport var parseNote = memoize(function(s) {\n var p = noteParse(s);\n return p ? encode(p.step, p.alt, p.oct) : null;\n});\n\n/**\n * Parse an interval\n * @function\n * @param {String} str\n * @return {Pitch} the pitch or null if not valid interval string\n */\nexport var parseIvl = memoize(function(s) {\n var p = ivlParse(s);\n if (!p) return null;\n return p ? encode(p.simple - 1, p.alt, p.oct, p.dir) : null;\n});\n\n/**\n * Parse a note or an interval\n * @param {String} str\n * @return {Pitch} the pitch or null if not valid pitch string\n */\nexport function parsePitch(s) {\n return parseNote(s) || parseIvl(s);\n}\n\n/**\n * Ensure the given object is a note pitch. If is a string, it will be\n * parsed. If not a note pitch or valid note string, it returns null.\n * @param {Pitch|String}\n * @return {Pitch}\n */\nexport function asNotePitch(p) {\n return isNotePitch(p) ? p : parseNote(p);\n}\n/**\n * Ensure the given object is a interval pitch. If is a string, it will be\n * parsed. If not a interval pitch or valid interval string, it returns null.\n * @param {Pitch|String}\n * @return {Pitch}\n */\nexport function asIvlPitch(p) {\n return isIvlPitch(p) ? p : parseIvl(p);\n}\n/**\n * Ensure the given object is a pitch. If is a string, it will be\n * parsed. If not a pitch or valid pitch string, it returns null.\n * @param {Pitch|String}\n * @return {Pitch}\n */\nexport function asPitch(p) {\n return isPitch(p) ? p : parsePitch(p);\n}\n\n/**\n * Convert a note pitch to string representation\n * @param {Pitch}\n * @return {String}\n */\nexport function strNote(p) {\n if (!isNotePitch(p)) return null;\n return noteStr.apply(null, decode(p));\n}\n\n/**\n * Convert a interval pitch to string representation\n * @param {Pitch}\n * @return {String}\n */\nexport function strIvl(p) {\n if (!isIvlPitch(p)) return null;\n // decode to [step, alt, oct]\n var d = decode(p);\n // d = [step, alt, oct]\n var num = d[0] + 1 + 7 * d[2];\n return p[2] * num + altToQ(num, d[1]);\n}\n\n/**\n * Convert a pitch to string representation (either notes or intervals)\n * @param {Pitch}\n * @return {String}\n */\nexport function strPitch(p) {\n return strNote(p) || strIvl(p);\n}\n\n// A function that creates a decorator\n// The returned function can _decorate_ other functions to parse and build\n// string representations\nfunction decorator(is, parse, str) {\n return function(fn) {\n return function(v) {\n var i = is(v);\n // if the value is in pitch notation no conversion\n if (i) return fn(v);\n // else parse the pitch\n var p = parse(v);\n // if parsed, apply function and back to string\n return p ? str(fn(p)) : null;\n };\n };\n}\n\n/**\n * Decorate a function to work internally with note pitches, even if the\n * parameters are provided as strings. Also it converts back the result\n * to string if a note pitch is returned.\n * @function\n * @param {Function} fn\n * @return {Function} the decorated function\n */\nexport var noteFn = decorator(isNotePitch, parseNote, strNote);\n/**\n * Decorate a function to work internally with interval pitches, even if the\n * parameters are provided as strings. Also it converts back the result\n * to string if a interval pitch is returned.\n * @function\n * @param {Function} fn\n * @return {Function} the decorated function\n */\nexport var ivlFn = decorator(isIvlPitch, parseIvl, strIvl);\n/**\n * Decorate a function to work internally with pitches, even if the\n * parameters are provided as strings. Also it converts back the result\n * to string if a pitch is returned.\n * @function\n * @param {Function} fn\n * @return {Function} the decorated function\n */\nexport var pitchFn = decorator(isPitch, parsePitch, strPitch);\n","/**\n * This module deals with note transposition. Just two functions: `transpose`\n * to transpose notes by any interval (or intervals by intervals) and `trFifths`\n * to transpose notes by fifths.\n *\n * @example\n * var tonal = require('tonal')\n * tonal.transpose('C3', 'P5') // => 'G3'\n * tonal.transpose('m2', 'P4') // => '5d'\n * tonal.trFifths('C', 2) // => 'D'\n *\n * @module transpose\n */\nimport {\n pitch,\n pType,\n fifths,\n focts,\n height,\n isPC,\n asPitch,\n isIvlPitch,\n strPitch\n} from \"tonal-pitch\";\n\nfunction trBy(i, p) {\n var t = pType(p);\n if (!t) return null;\n var f = fifths(i) + fifths(p);\n if (isPC(p)) return [\"tnlp\", [f]];\n var o = focts(i) + focts(p);\n if (t === \"note\") return [\"tnlp\", [f, o]];\n var d = height(i) + height(p) < 0 ? -1 : 1;\n return [\"tnlp\", [d * f, d * o], d];\n}\n\n/**\n * Transpose notes. Can be used to add intervals. At least one of the parameter\n * is expected to be an interval. If not, it returns null.\n *\n * @param {String|Pitch} a - a note or interval\n * @param {String|Pitch} b - a note or interavl\n * @return {String|Pitch} the transposed pitch or null if not valid parameters\n * @example\n * var _ = require('tonal')\n * // transpose a note by an interval\n * _.transpose('d3', '3M') // => 'F#3'\n * // transpose intervals\n * _.transpose('3m', '5P') // => '7m'\n * // it works with pitch classes\n * _.transpose('d', '3M') // => 'F#'\n * // order or parameters is irrelevant\n * _.transpose('3M', 'd3') // => 'F#3'\n * // can be partially applied\n * _.map(_.transpose('3M'), 'c d e f g') // => ['E', 'F#', 'G#', 'A', 'B']\n */\nexport function transpose(a, b) {\n if (arguments.length === 1)\n return function(b) {\n return transpose(a, b);\n };\n var pa = asPitch(a);\n var pb = asPitch(b);\n var r = isIvlPitch(pa) ? trBy(pa, pb) : isIvlPitch(pb) ? trBy(pb, pa) : null;\n return a === pa && b === pb ? r : strPitch(r);\n}\n\n/**\n * Transpose a tonic a number of perfect fifths. It can be partially applied.\n *\n * @function\n * @param {Pitch|String} tonic\n * @param {Integer} number - the number of times\n * @return {String|Pitch} the transposed note\n * @example\n * import { trFifths } from 'tonal-transpose'\n * [0, 1, 2, 3, 4].map(trFifths('C')) // => ['C', 'G', 'D', 'A', 'E']\n * // or using tonal\n * tonal.trFifths('G4', 1) // => 'D5'\n */\nexport function trFifths(t, n) {\n if (arguments.length > 1) return trFifths(t)(n);\n return function(n) {\n return transpose(t, pitch(n, 0, 1));\n };\n}\n","/**\n * Functions to calculate distances between notes\n *\n * @example\n * // using node's require\n * var distance = require('tonal-distance')\n * distance.interval('C4', 'G4') // => '5P'\n *\n * @example\n * // using ES6 import\n * import { interval, semitones } from 'tonal-distance'\n * semitones('C' ,'D') // => 2\n *\n * @module distance\n */\nimport {\n isPC,\n fifths,\n focts,\n pitch,\n height,\n asPitch,\n strIvl\n} from \"tonal-pitch\";\n\n// substract two pitches\nfunction substr(a, b) {\n if (!a || !b || a[1].length !== b[1].length) return null;\n var f = fifths(b) - fifths(a);\n if (isPC(a)) return pitch(f, -Math.floor(f * 7 / 12), 1);\n var o = focts(b) - focts(a);\n var d = height(b) - height(a) < 0 ? -1 : 1;\n return pitch(d * f, d * o, d);\n}\n\n/**\n * Find the interval between two pitches. Both pitches MUST be of the same type:\n *\n * - notes: it returns the interval between the first and the second\n * - pitch classes: it returns the __ascending__ interval between both\n * - intervals: substract one from the other\n *\n * @param {Pitch|String} from - distance from\n * @param {Pitch|String} to - distance to\n * @return {Interval} the distance between pitches\n *\n * @example\n * var distance = require('tonal-distance')\n * distance.interval('C2', 'C3') // => 'P8'\n * distance.interval('G', 'B') // => 'M3'\n * // or use tonal\n * var tonal = require('tonal')\n * tonal.distance.interval('M2', 'P5') // => 'P4'\n */\nexport function interval(a, b) {\n if (arguments.length === 1)\n return function(b) {\n return interval(a, b);\n };\n var pa = asPitch(a);\n var pb = asPitch(b);\n var i = substr(pa, pb);\n // if a and b are in array notation, no conversion back\n return a === pa && b === pb ? i : strIvl(i);\n}\n\n/**\n * Get the distance between two notes in semitones\n * @param {String|Pitch} from - first note\n * @param {String|Pitch} to - last note\n * @return {Integer} the distance in semitones or null if not valid notes\n * @example\n * import { semitones } from 'tonal-distance'\n * semitones('C3', 'A2') // => -3\n * // or use tonal\n * tonal.distance.semitones('C3', 'G3') // => 7\n */\nexport function semitones(a, b) {\n var i = substr(asPitch(a), asPitch(b));\n return i ? height(i) : null;\n}\n","/**\n * This module implements utility functions related to array manipulation, like:\n * `map`, `filter`, `shuffle`, `sort`, `rotate`, `select`\n *\n * All the functions are _functional friendly_ with target object as last\n * parameter and currified. The sorting functions understand about pitch\n * heights and interval sizes.\n *\n * One key feature of tonal is that you can represent lists with arrays or\n * with space separated string of elements. This module implements that\n * functionallity.\n *\n * @module array\n */\nimport { asPitch, isPitch, strPitch, pitch, fifths, focts } from \"tonal-pitch\";\nimport { transpose as tr } from \"tonal-transpose\";\nimport { semitones } from \"tonal-distance\";\n\nfunction split(sep) {\n return function(o) {\n return o === undefined\n ? []\n : Array.isArray(o)\n ? o\n : typeof o === \"string\" ? o.trim().split(sep) : [o];\n };\n}\n\n// utility\nvar isArr = Array.isArray;\nfunction hasVal(e) {\n return e || e === 0;\n}\n\n/**\n * Convert anything to array. Speifically, split string separated by spaces,\n * commas or bars. If you give it an actual array, it returns it without\n * modification.\n *\n * This function __always__ returns an array (null or undefined values are converted\n * to empty arrays)\n *\n * Thanks to this function, the rest of the functions of this module accepts\n * strings as an array parameter.\n *\n * @function\n * @param {*} source - the thing to get an array from\n * @return {Array} the object as an array\n *\n * @example\n * import { asArr } from 'tonal-arrays'\n * asArr('C D E F G') // => ['C', 'D', 'E', 'F', 'G']\n * asArr('A, B, c') // => ['A', 'B', 'c']\n * asArr('1 | 2 | x') // => ['1', '2', 'x']\n */\nexport var asArr = split(/\\s*\\|\\s*|\\s*,\\s*|\\s+/);\n\n/**\n * Return a new array with the elements mapped by a function.\n * Basically the same as the JavaScript standard `array.map` but with\n * two enhacements:\n *\n * - Arrays can be expressed as strings (see [asArr])\n * - This function can be partially applied. This is useful to create _mapped_\n * versions of single element functions. For an excellent introduction of\n * the adventages [read this](https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch4.html)\n *\n * @param {Function} fn - the function\n * @param {Array|String} arr - the array to be mapped\n * @return {Array}\n * @example\n * var arr = require('tonal-arr')\n * var toUp = arr.map(function(e) { return e.toUpperCase() })\n * toUp('a b c') // => ['A', 'B', 'C']\n *\n * @example\n * var tonal = require('tonal')\n * tonal.map(tonal.transpose('M3'), 'C D E') // => ['E', 'F#', 'G#']\n */\nexport function map(fn, list) {\n return arguments.length > 1\n ? map(fn)(list)\n : function(l) {\n return asArr(l).map(fn);\n };\n}\n\n/**\n * Return a copy of the array with the null values removed\n * @param {String|Array} list\n * @return {Array}\n * @example\n * tonal.compact(['a', 'b', null, 'c']) // => ['a', 'b', 'c']\n */\nexport function compact(arr) {\n return asArr(arr).filter(hasVal);\n}\n\n/**\n * Filter an array with a function. Again, almost the same as JavaScript standard\n * filter function but:\n *\n * - It accepts strings as arrays\n * - Can be partially applied\n *\n * @param {Function} fn\n * @param {String|Array} arr\n * @return {Array}\n * @example\n * t.filter(t.noteName, 'a b c x bb') // => [ 'a', 'b', 'c', 'bb' ]\n */\nexport function filter(fn, list) {\n return arguments.length > 1\n ? filter(fn)(list)\n : function(l) {\n return asArr(l).filter(fn);\n };\n}\n\n// a custom height function that\n// - returns -Infinity for non-pitch objects\n// - assumes pitch classes has octave -100 (so are sorted before that notes)\nfunction objHeight(p) {\n if (!p) return -Infinity;\n var f = fifths(p) * 7;\n var o = focts(p) || -Math.floor(f / 12) - 100;\n return f + o * 12;\n}\n\n// ascending comparator\nfunction ascComp(a, b) {\n return objHeight(a) - objHeight(b);\n}\n// descending comparator\nfunction descComp(a, b) {\n return -ascComp(a, b);\n}\n\n/**\n * Sort a list of notes or intervals in ascending or descending pitch order.\n * It removes from the list any thing is not a pitch (a note or interval)\n *\n * Note this function returns a __copy__ of the array, it does NOT modify\n * the original.\n *\n * @param {Array|String} list - the list of notes or intervals\n * @param {Boolean|Function} comp - (Optional) comparator.\n * Ascending pitch by default. Pass a `false` to order descending\n * or a custom comparator function (that receives pitches in array notation).\n * Note that any other value is ignored.\n * @example\n * array.sort('D E C') // => ['C', 'D', 'E']\n * array.sort('D E C', false) // => ['E', 'D', 'C']\n * // if is not a note, it wil be removed\n * array.sort('g h f i c') // => ['C', 'F', 'G']\n */\nexport function sort(list, comp) {\n var fn =\n arguments.length === 1 || comp === true\n ? ascComp\n : comp === false ? descComp : typeof comp === \"function\" ? comp : ascComp;\n // if the list is an array, make a copy\n list = Array.isArray(list) ? list.slice() : asArr(list);\n return listFn(function(arr) {\n return arr.sort(fn).filter(hasVal);\n }, list);\n}\n\n/**\n * Randomizes the order of the specified array using the Fisher–Yates shuffle.\n *\n * @function\n * @param {Array|String} arr - the array\n * @return {Array} the shuffled array\n *\n * @example\n * import { shuffle } from 'tonal-arrays'\n * @example\n * var tonal = require('tonal')\n * tonal.shuffle('C D E F')\n */\nexport var shuffle = listFn(function(arr) {\n var i, t;\n var m = arr.length;\n while (m) {\n i = (Math.random() * m--) | 0;\n t = arr[m];\n arr[m] = arr[i];\n arr[i] = t;\n }\n return arr;\n});\n\nfunction trOct(n) {\n return tr(pitch(0, n, 1));\n}\n\n/**\n * Rotates a list a number of times. It's completly agnostic about the\n * contents of the list.\n * @param {Integer} times - the number of rotations\n * @param {Array|String} list - the list to be rotated\n * @return {Array} the rotated array\n */\nexport function rotate(times, list) {\n var arr = asArr(list);\n var len = arr.length;\n var n = (times % len + len) % len;\n return arr.slice(n, len).concat(arr.slice(0, n));\n}\n\n/**\n * Rotates an ascending list of pitches n times keeping the ascending property.\n * This functions assumes the list is an ascending list of pitches, and\n * transposes the them to ensure they are ascending after rotation.\n * It can be used, for example, to invert chords.\n *\n * @param {Integer} times - the number of rotations\n * @param {Array|String} list - the list to be rotated\n * @return {Array} the rotated array\n */\nexport function rotateAsc(times, list) {\n return listFn(function(arr) {\n var len = arr.length;\n var n = (times % len + len) % len;\n var head = arr.slice(n, len);\n var tail = arr.slice(0, n);\n // See if the first note of tail is lower than the last of head\n var s = semitones(head[len - n - 1], tail[0]);\n if (s < 0) {\n var octs = Math.floor(s / 12);\n if (times < 0) head = head.map(trOct(octs));\n else tail = tail.map(trOct(-octs));\n }\n return head.concat(tail);\n }, list);\n}\n\n/**\n * Select elements from a list.\n *\n * @param {String|Array} numbers - a __1-based__ index of the elements\n * @param {String|Array} list - the list of pitches\n * @return {Array} the selected elements (with nulls if not valid index)\n *\n * @example\n * import { select } from 'tonal-array'\n * select('1 3 5', 'C D E F G A B') // => ['C', 'E', 'G']\n * select('-1 0 1 2 3', 'C D') // => [ null, null, 'C', 'D', null ]\n */\nexport function select(nums, list) {\n if (arguments.length === 1) {\n return function(l) {\n return select(nums, l);\n };\n }\n var arr = asArr(list);\n return asArr(nums).map(function(n) {\n return arr[n - 1] || null;\n });\n}\n\n// http://stackoverflow.com/questions/9960908/permutations-in-javascript\n/**\n * Get all permutations of a list\n * @param {Array|Strng} list - the list\n * @return {Array} an array with all the permutations\n */\nexport function permutations(list) {\n list = asArr(list);\n if (list.length === 0) return [[]];\n return permutations(list.slice(1)).reduce(function(acc, perm) {\n return acc.concat(\n list.map(function(e, pos) {\n var newPerm = perm.slice();\n newPerm.splice(pos, 0, list[0]);\n return newPerm;\n })\n );\n }, []);\n}\n\n// #### Transform lists in array notation\nfunction asPitchStr(p) {\n return strPitch(p) || p;\n}\nfunction listToStr(v) {\n return isPitch(v) ? strPitch(v) : isArr(v) ? v.map(asPitchStr) : v;\n}\n\n/**\n * Decorates a function to so it's first parameter is an array of pitches in\n * array notation. Also, if the return value is a pitch or an array of pitches\n * in array notation, it convert backs to strings.\n *\n * @private\n * @param {Function} fn - the function to decorate\n * @return {Function} the decorated function\n * @example\n * import { listFn } from 'tonal-arrays'\n * var octUp = listFn((p) => { p[2] = p[2] + 1; return p[2] })\n * octUp('C2 D2 E2') // => ['C3', 'D3', 'E3']\n */\nfunction listFn(fn, list) {\n if (arguments.length === 1) {\n return function(l) {\n return listFn(fn, l);\n };\n }\n var arr = asArr(list).map(asPitch);\n var res = fn(arr);\n return listToStr(res);\n}\n","/**\n * Functions to transpose o calculate distances from a collection of notes.\n *\n * A useful concept is _harmonizer_: a function that _harmonizes_ notes. It can\n * be created by partially applying the `harmonize` function (see examples)\n *\n * @example\n * var harmonizer = require('tonal-harmonizer')\n * harmonizer.harmonize('1P 3M 5P', 'C') // => ['C', 'E', 'G']\n * var maj7 = harmonizer.harmonize('1P 3M 5P 7M')\n * maj7('D4') // => ['D4', 'F#4', 'A4', 'C#5']\n * harmonizer.harmonics('C E G') // => ['1P', '3M', '5P']\n *\n * @example\n * // in tonal this functions are NOT namespaced\n * var tonal = require('tonal')\n * tonal.harmonize('1P 3M 5P', 'G')\n *\n * @example\n * // using ES6 import syntax\n * import { harmonize } from 'tonal-harmonizer'\n * harmonize(...)\n *\n * @module harmonizer\n */\nimport { transpose as tr } from \"tonal-transpose\";\nimport { interval } from \"tonal-distance\";\nimport { asArr, map, compact } from \"tonal-array\";\n\n/**\n * Given a list of notes, return the distance from the first note to the rest.\n * @param {Array|String} notes - the list of notes\n * @return {Array} the intervals relative to the first note\n * @example\n * harmonizer.harmonics('C E G') // => ['1P', '3M', '5P']\n *\n * @example\n * // in tonal this functions are NOT namespaced\n * tonal.harmonics(tonal.scale('C major')) // => ['1P', ...]\n */\nexport function harmonics(list) {\n var a = asArr(list);\n return a.length ? compact(a.map(interval(a[0]))) : a;\n}\n\n/**\n * Given a list of notes, return the intervallic structure: the distance from\n * one to the next.\n *\n * Notice that the number of intervals is one less that the number of notes.\n *\n * @param {Array|String} notes - the list of notes\n * @return {Array} the intervals relative to the previous\n * @example\n * harmonizer.intervallic('c e g') // => ['3M', '3m']\n * harmonizer.intervallic('e g c') // => ['3m', '4P']\n * harmonizer.intervallic('c') // => []\n */\nexport function intervallic(notes) {\n var dist = [];\n notes = asArr(notes);\n for (var i = 1; i < notes.length; i++) {\n dist.push(interval(notes[i - 1], notes[i]));\n }\n return dist;\n}\n\n/**\n * Given a list of intervals and a tonic, return that tonic transposed\n * to that intervals.\n *\n * It's currified and, calling with only one parameter, returns an harmonizer,\n * a function that harmonizes any note (see example)\n *\n * @function\n * @param {String|Array} list - the list of intervals\n * @param {String|Pitch} note - the note to be harmonized\n * @return {Array} the resulting notes\n * @example\n * harmonizer.harmonize('P1 M3 P5 M7', 'C') // => ['C', 'E', 'G', 'B']\n * @example\n * // harmonizer with partial application\n * var maj7 = harmonize.harmonizer('P1 M3 P5 M7')\n * maj7('C') // => ['C', 'E', 'G', 'B']\n * @example\n * // in tonal this function is NOT namespaced\n * var C = tonal.harmonizer('C D E')\n * C('M3') // => ['E', 'G#', 'B']\n */\nexport function harmonize(list, pitch) {\n if (arguments.length > 1) return harmonize(list)(pitch);\n return function(tonic) {\n return compact(map(tr(tonic || \"P1\"), list));\n };\n}\n","'use strict'\n\n// util\nfunction fillStr (s, num) { return Array(num + 1).join(s) }\nfunction isNum (x) { return typeof x === 'number' }\nfunction isStr (x) { return typeof x === 'string' }\nfunction isDef (x) { return typeof x !== 'undefined' }\nfunction midiToFreq (midi, tuning) {\n return Math.pow(2, (midi - 69) / 12) * (tuning || 440)\n}\n\nvar REGEX = /^([a-gA-G])(#{1,}|b{1,}|x{1,}|)(-?\\d*)\\s*(.*)\\s*$/\n/**\n * A regex for matching note strings in scientific notation.\n *\n * @name regex\n * @function\n * @return {RegExp} the regexp used to parse the note name\n *\n * The note string should have the form `letter[accidentals][octave][element]`\n * where:\n *\n * - letter: (Required) is a letter from A to G either upper or lower case\n * - accidentals: (Optional) can be one or more `b` (flats), `#` (sharps) or `x` (double sharps).\n * They can NOT be mixed.\n * - octave: (Optional) a positive or negative integer\n * - element: (Optional) additionally anything after the duration is considered to\n * be the element name (for example: 'C2 dorian')\n *\n * The executed regex contains (by array index):\n *\n * - 0: the complete string\n * - 1: the note letter\n * - 2: the optional accidentals\n * - 3: the optional octave\n * - 4: the rest of the string (trimmed)\n *\n * @example\n * var parser = require('note-parser')\n * parser.regex.exec('c#4')\n * // => ['c#4', 'c', '#', '4', '']\n * parser.regex.exec('c#4 major')\n * // => ['c#4major', 'c', '#', '4', 'major']\n * parser.regex().exec('CMaj7')\n * // => ['CMaj7', 'C', '', '', 'Maj7']\n */\nexport function regex () { return REGEX }\n\nvar SEMITONES = [0, 2, 4, 5, 7, 9, 11]\n/**\n * Parse a note name in scientific notation an return it's components,\n * and some numeric properties including midi number and frequency.\n *\n * @name parse\n * @function\n * @param {String} note - the note string to be parsed\n * @param {Boolean} isTonic - true the strings it's supposed to contain a note number\n * and some category (for example an scale: 'C# major'). It's false by default,\n * but when true, en extra tonicOf property is returned with the category ('major')\n * @param {Float} tunning - The frequency of A4 note to calculate frequencies.\n * By default it 440.\n * @return {Object} the parsed note name or null if not a valid note\n *\n * The parsed note name object will ALWAYS contains:\n * - letter: the uppercase letter of the note\n * - acc: the accidentals of the note (only sharps or flats)\n * - pc: the pitch class (letter + acc)\n * - step: s a numeric representation of the letter. It's an integer from 0 to 6\n * where 0 = C, 1 = D ... 6 = B\n * - alt: a numeric representation of the accidentals. 0 means no alteration,\n * positive numbers are for sharps and negative for flats\n * - chroma: a numeric representation of the pitch class. It's like midi for\n * pitch classes. 0 = C, 1 = C#, 2 = D ... 11 = B. Can be used to find enharmonics\n * since, for example, chroma of 'Cb' and 'B' are both 11\n *\n * If the note has octave, the parser object will contain:\n * - oct: the octave number (as integer)\n * - midi: the midi number\n * - freq: the frequency (using tuning parameter as base)\n *\n * If the parameter `isTonic` is set to true, the parsed object will contain:\n * - tonicOf: the rest of the string that follows note name (left and right trimmed)\n *\n * @example\n * var parse = require('note-parser').parse\n * parse('Cb4')\n * // => { letter: 'C', acc: 'b', pc: 'Cb', step: 0, alt: -1, chroma: -1,\n * oct: 4, midi: 59, freq: 246.94165062806206 }\n * // if no octave, no midi, no freq\n * parse('fx')\n * // => { letter: 'F', acc: '##', pc: 'F##', step: 3, alt: 2, chroma: 7 })\n */\nexport function parse (str, isTonic, tuning) {\n if (typeof str !== 'string') return null\n var m = REGEX.exec(str)\n if (!m || (!isTonic && m[4])) return null\n\n var p = { letter: m[1].toUpperCase(), acc: m[2].replace(/x/g, '##') }\n p.pc = p.letter + p.acc\n p.step = (p.letter.charCodeAt(0) + 3) % 7\n p.alt = p.acc[0] === 'b' ? -p.acc.length : p.acc.length\n var pos = SEMITONES[p.step] + p.alt\n p.chroma = pos < 0 ? 12 + pos : pos % 12\n if (m[3]) { // has octave\n p.oct = +m[3]\n p.midi = pos + 12 * (p.oct + 1)\n p.freq = midiToFreq(p.midi, tuning)\n }\n if (isTonic) p.tonicOf = m[4]\n return p\n}\n\nvar LETTERS = 'CDEFGAB'\nfunction accStr (n) { return !isNum(n) ? '' : n < 0 ? fillStr('b', -n) : fillStr('#', n) }\nfunction octStr (n) { return !isNum(n) ? '' : '' + n }\n\n/**\n * Create a string from a parsed object or `step, alteration, octave` parameters\n * @param {Object} obj - the parsed data object\n * @return {String} a note string or null if not valid parameters\n * @since 1.2\n * @example\n * parser.build(parser.parse('cb2')) // => 'Cb2'\n *\n * @example\n * // it accepts (step, alteration, octave) parameters:\n * parser.build(3) // => 'F'\n * parser.build(3, -1) // => 'Fb'\n * parser.build(3, -1, 4) // => 'Fb4'\n */\nexport function build (s, a, o) {\n if (s === null || typeof s === 'undefined') return null\n if (s.step) return build(s.step, s.alt, s.oct)\n if (s < 0 || s > 6) return null\n return LETTERS.charAt(s) + accStr(a) + octStr(o)\n}\n\n/**\n * Get midi of a note\n *\n * @name midi\n * @function\n * @param {String|Integer} note - the note name or midi number\n * @return {Integer} the midi number of the note or null if not a valid note\n * or the note does NOT contains octave\n * @example\n * var parser = require('note-parser')\n * parser.midi('A4') // => 69\n * parser.midi('A') // => null\n * @example\n * // midi numbers are bypassed (even as strings)\n * parser.midi(60) // => 60\n * parser.midi('60') // => 60\n */\nexport function midi (note) {\n if ((isNum(note) || isStr(note)) && note >= 0 && note < 128) return +note\n var p = parse(note)\n return p && isDef(p.midi) ? p.midi : null\n}\n\n/**\n * Get freq of a note in hertzs (in a well tempered 440Hz A4)\n *\n * @name freq\n * @function\n * @param {String} note - the note name or note midi number\n * @param {String} tuning - (Optional) the A4 frequency (440 by default)\n * @return {Float} the freq of the number if hertzs or null if not valid note\n * @example\n * var parser = require('note-parser')\n * parser.freq('A4') // => 440\n * parser.freq('A') // => null\n * @example\n * // can change tuning (440 by default)\n * parser.freq('A4', 444) // => 444\n * parser.freq('A3', 444) // => 222\n * @example\n * // it accepts midi numbers (as numbers and as strings)\n * parser.freq(69) // => 440\n * parser.freq('69', 442) // => 442\n */\nexport function freq (note, tuning) {\n var m = midi(note)\n return m === null ? null : midiToFreq(m, tuning)\n}\n\nexport function letter (src) { return (parse(src) || {}).letter }\nexport function acc (src) { return (parse(src) || {}).acc }\nexport function pc (src) { return (parse(src) || {}).pc }\nexport function step (src) { return (parse(src) || {}).step }\nexport function alt (src) { return (parse(src) || {}).alt }\nexport function chroma (src) { return (parse(src) || {}).chroma }\nexport function oct (src) { return (parse(src) || {}).oct }\n","'use strict'\n\n// util\nfunction fillStr (s, num) { return Array(num + 1).join(s) }\nfunction isNum (x) { return typeof x === 'number' }\nfunction isStr (x) { return typeof x === 'string' }\nfunction isDef (x) { return typeof x !== 'undefined' }\nfunction midiToFreq (midi, tuning) {\n return Math.pow(2, (midi - 69) / 12) * (tuning || 440)\n}\n\nvar REGEX = /^([a-gA-G])(#{1,}|b{1,}|x{1,}|)(-?\\d*)\\s*(.*)\\s*$/\n/**\n * A regex for matching note strings in scientific notation.\n *\n * @name regex\n * @function\n * @return {RegExp} the regexp used to parse the note name\n *\n * The note string should have the form `letter[accidentals][octave][element]`\n * where:\n *\n * - letter: (Required) is a letter from A to G either upper or lower case\n * - accidentals: (Optional) can be one or more `b` (flats), `#` (sharps) or `x` (double sharps).\n * They can NOT be mixed.\n * - octave: (Optional) a positive or negative integer\n * - element: (Optional) additionally anything after the duration is considered to\n * be the element name (for example: 'C2 dorian')\n *\n * The executed regex contains (by array index):\n *\n * - 0: the complete string\n * - 1: the note letter\n * - 2: the optional accidentals\n * - 3: the optional octave\n * - 4: the rest of the string (trimmed)\n *\n * @example\n * var parser = require('note-parser')\n * parser.regex.exec('c#4')\n * // => ['c#4', 'c', '#', '4', '']\n * parser.regex.exec('c#4 major')\n * // => ['c#4major', 'c', '#', '4', 'major']\n * parser.regex().exec('CMaj7')\n * // => ['CMaj7', 'C', '', '', 'Maj7']\n */\nexport function regex () { return REGEX }\n\nvar SEMITONES = [0, 2, 4, 5, 7, 9, 11]\n/**\n * Parse a note name in scientific notation an return it's components,\n * and some numeric properties including midi number and frequency.\n *\n * @name parse\n * @function\n * @param {String} note - the note string to be parsed\n * @param {Boolean} isTonic - true the strings it's supposed to contain a note number\n * and some category (for example an scale: 'C# major'). It's false by default,\n * but when true, en extra tonicOf property is returned with the category ('major')\n * @param {Float} tunning - The frequency of A4 note to calculate frequencies.\n * By default it 440.\n * @return {Object} the parsed note name or null if not a valid note\n *\n * The parsed note name object will ALWAYS contains:\n * - letter: the uppercase letter of the note\n * - acc: the accidentals of the note (only sharps or flats)\n * - pc: the pitch class (letter + acc)\n * - step: s a numeric representation of the letter. It's an integer from 0 to 6\n * where 0 = C, 1 = D ... 6 = B\n * - alt: a numeric representation of the accidentals. 0 means no alteration,\n * positive numbers are for sharps and negative for flats\n * - chroma: a numeric representation of the pitch class. It's like midi for\n * pitch classes. 0 = C, 1 = C#, 2 = D ... 11 = B. Can be used to find enharmonics\n * since, for example, chroma of 'Cb' and 'B' are both 11\n *\n * If the note has octave, the parser object will contain:\n * - oct: the octave number (as integer)\n * - midi: the midi number\n * - freq: the frequency (using tuning parameter as base)\n *\n * If the parameter `isTonic` is set to true, the parsed object will contain:\n * - tonicOf: the rest of the string that follows note name (left and right trimmed)\n *\n * @example\n * var parse = require('note-parser').parse\n * parse('Cb4')\n * // => { letter: 'C', acc: 'b', pc: 'Cb', step: 0, alt: -1, chroma: -1,\n * oct: 4, midi: 59, freq: 246.94165062806206 }\n * // if no octave, no midi, no freq\n * parse('fx')\n * // => { letter: 'F', acc: '##', pc: 'F##', step: 3, alt: 2, chroma: 7 })\n */\nexport function parse (str, isTonic, tuning) {\n if (typeof str !== 'string') return null\n var m = REGEX.exec(str)\n if (!m || (!isTonic && m[4])) return null\n\n var p = { letter: m[1].toUpperCase(), acc: m[2].replace(/x/g, '##') }\n p.pc = p.letter + p.acc\n p.step = (p.letter.charCodeAt(0) + 3) % 7\n p.alt = p.acc[0] === 'b' ? -p.acc.length : p.acc.length\n var pos = SEMITONES[p.step] + p.alt\n p.chroma = pos < 0 ? 12 + pos : pos % 12\n if (m[3]) { // has octave\n p.oct = +m[3]\n p.midi = pos + 12 * (p.oct + 1)\n p.freq = midiToFreq(p.midi, tuning)\n }\n if (isTonic) p.tonicOf = m[4]\n return p\n}\n\nvar LETTERS = 'CDEFGAB'\nfunction accStr (n) { return !isNum(n) ? '' : n < 0 ? fillStr('b', -n) : fillStr('#', n) }\nfunction octStr (n) { return !isNum(n) ? '' : '' + n }\n\n/**\n * Create a string from a parsed object or `step, alteration, octave` parameters\n * @param {Object} obj - the parsed data object\n * @return {String} a note string or null if not valid parameters\n * @since 1.2\n * @example\n * parser.build(parser.parse('cb2')) // => 'Cb2'\n *\n * @example\n * // it accepts (step, alteration, octave) parameters:\n * parser.build(3) // => 'F'\n * parser.build(3, -1) // => 'Fb'\n * parser.build(3, -1, 4) // => 'Fb4'\n */\nexport function build (s, a, o) {\n if (s === null || typeof s === 'undefined') return null\n if (s.step) return build(s.step, s.alt, s.oct)\n if (s < 0 || s > 6) return null\n return LETTERS.charAt(s) + accStr(a) + octStr(o)\n}\n\n/**\n * Get midi of a note\n *\n * @name midi\n * @function\n * @param {String|Integer} note - the note name or midi number\n * @return {Integer} the midi number of the note or null if not a valid note\n * or the note does NOT contains octave\n * @example\n * var parser = require('note-parser')\n * parser.midi('A4') // => 69\n * parser.midi('A') // => null\n * @example\n * // midi numbers are bypassed (even as strings)\n * parser.midi(60) // => 60\n * parser.midi('60') // => 60\n */\nexport function midi (note) {\n if ((isNum(note) || isStr(note)) && note >= 0 && note < 128) return +note\n var p = parse(note)\n return p && isDef(p.midi) ? p.midi : null\n}\n\n/**\n * Get freq of a note in hertzs (in a well tempered 440Hz A4)\n *\n * @name freq\n * @function\n * @param {String} note - the note name or note midi number\n * @param {String} tuning - (Optional) the A4 frequency (440 by default)\n * @return {Float} the freq of the number if hertzs or null if not valid note\n * @example\n * var parser = require('note-parser')\n * parser.freq('A4') // => 440\n * parser.freq('A') // => null\n * @example\n * // can change tuning (440 by default)\n * parser.freq('A4', 444) // => 444\n * parser.freq('A3', 444) // => 222\n * @example\n * // it accepts midi numbers (as numbers and as strings)\n * parser.freq(69) // => 440\n * parser.freq('69', 442) // => 442\n */\nexport function freq (note, tuning) {\n var m = midi(note)\n return m === null ? null : midiToFreq(m, tuning)\n}\n\nexport function letter (src) { return (parse(src) || {}).letter }\nexport function acc (src) { return (parse(src) || {}).acc }\nexport function pc (src) { return (parse(src) || {}).pc }\nexport function step (src) { return (parse(src) || {}).step }\nexport function alt (src) { return (parse(src) || {}).alt }\nexport function chroma (src) { return (parse(src) || {}).chroma }\nexport function oct (src) { return (parse(src) || {}).oct }\n","/**\n * A midi note number is a number representation of a note pitch. It can be\n * integers so it's equal tempered tuned, or float to indicate it's not\n * tuned into equal temepered scale.\n *\n * This module contains functions to convert to and from midi notes.\n *\n * @example\n * var midi = require('tonal-midi')\n * midi.toMidi('A4') // => 69\n * midi.note(69) // => 'A4'\n * midi.note(61) // => 'Db4'\n * midi.note(61, true) // => 'C#4'\n *\n * @module midi\n */\n\nimport { midi } from \"note-parser\";\n\n/**\n * Convert the given note to a midi note number. If you pass a midi number it\n * will returned as is.\n *\n * @param {Array|String|Number} note - the note to get the midi number from\n * @return {Integer} the midi number or null if not valid pitch\n * @example\n * midi.toMidi('C4') // => 60\n * midi.toMidi(60) // => 60\n * midi.toMidi('60') // => 60\n */\nexport function toMidi(val) {\n if (Array.isArray(val) && val.length === 2)\n return val[0] * 7 + val[1] * 12 + 12;\n return midi(val);\n}\n\nvar FLATS = \"C Db D Eb E F Gb G Ab A Bb B\".split(\" \");\nvar SHARPS = \"C C# D D# E F F# G G# A A# B\".split(\" \");\n\n/**\n * Given a midi number, returns a note name. The altered notes will have\n * flats unless explicitly set with the optional `useSharps` parameter.\n *\n * @function\n * @param {Integer} midi - the midi note number\n * @param {Boolean} useSharps - (Optional) set to true to use sharps instead of flats\n * @return {String} the note name\n * @example\n * var midi = require('tonal-midi')\n * midi.note(61) // => 'Db4'\n * midi.note(61, true) // => 'C#4'\n * // it rounds to nearest note\n * midi.note(61.7) // => 'D4'\n */\nexport function note(num, sharps) {\n if (num === true || num === false)\n return function(m) {\n return note(m, num);\n };\n num = Math.round(num);\n var pcs = sharps === true ? SHARPS : FLATS;\n var pc = pcs[num % 12];\n var o = Math.floor(num / 12) - 1;\n return pc + o;\n}\n","/**\n * [![npm version](https://img.shields.io/npm/v/tonal-freq.svg)](https://www.npmjs.com/package/tonal-freq)\n * [![tonal](https://img.shields.io/badge/tonal-freq-yellow.svg)](https://www.npmjs.com/browse/keyword/tonal)\n *\n * `tonal-freq` is a collection of functions to perform calculations related to frequencies.\n *\n * This is part of [tonal](https://www.npmjs.com/package/tonal) music theory library.\n *\n * ## Usage\n *\n * ```js\n * var freq = require('tonal-freq')\n * freq.toFreq('A4') // => 440\n * freq.note(440) // => 'A4'\n * freq.noteAndDetune(320) // => ['C4', 200]\n * ```\n *\n * ## Install\n *\n * [![npm install tonal-freq](https://nodei.co/npm/tonal-freq.png?mini=true)](https://npmjs.org/package/tonal-freq/)\n *\n * ## API Documentation\n *\n * @module freq\n */\nimport { toMidi as noteToMidi, note as midiToNote } from \"tonal-midi\";\n\n// decorate a function to round the numeric result to a max\nfunction round(m, fn) {\n m = m || m === 0 ? Math.pow(10, m) : false;\n return function(v) {\n v = fn(v);\n return v === null ? null : m ? Math.round(v * m) / m : v;\n };\n}\n\n/**\n * Return the equal tempered frequency of a note.\n *\n * This function can be partially applied if note parameter is not present.\n * @function\n * @param {Float} ref - the tuning reference\n * @param {Integer} maxDecimals - (Optional) the maximum number of decimals (all by default)\n * @param {String|Pitch} note - the note to get the frequency from\n * @return {Number} the frequency\n * @example\n * eqTempFreq(444, 4, 'C3')\n * const toFreq = eqTempFreq(444, 2)\n * toFreq('A3') // => 222\n */\nexport function eqTempFreq(ref, max, note) {\n if (arguments.length > 2) return eqTempFreq(ref, max)(note);\n return round(max, function(p) {\n var m = noteToMidi(p);\n return m ? Math.pow(2, (m - 69) / 12) * ref : null;\n });\n}\n\n/**\n * Get the frequency of note with 2 decimals precission using A4 440Hz tuning\n *\n * This is an alias for: `eqTempFreq(440, 2, )`\n *\n * @function\n * @param {Number|String} note - the note name or midi number\n * @return {Float} the frequency in herzs\n * @example\n * freq.toFreq('A4') // => 440\n * freq.toFreq('C4') // => 261.63\n */\nexport var toFreq = eqTempFreq(440, 2);\n\n/**\n * Get the midi note from a frequency in equal temperament scale. You can\n * specify the number of decimals of the midi number.\n *\n * @param {Float} tuning - (Optional) the reference A4 tuning (440Hz by default)\n * @param {Number} freq - the frequency\n * @return {Number} the midi number\n */\nexport function eqTempFreqToMidi(ref, max, freq) {\n if (arguments.length > 2) return eqTempFreqToMidi(ref, max)(freq);\n return round(max, function(freq) {\n return 12 * (Math.log(freq) - Math.log(ref)) / Math.log(2) + 69;\n });\n}\n\n/**\n * Get midi number from frequency with two decimals of precission.\n *\n * This is an alisas for: `eqTempFreqToMidi(440, 2, )`\n *\n * @function\n * @param {Float} freq\n * @return {Number} midi number\n * @example\n * freq.toMidi(361) // => 59.96\n */\nexport var toMidi = eqTempFreqToMidi(440, 2);\n\n/**\n * Get note name from frequency using an equal temperament scale with 440Hz\n * as reference\n *\n * @param {Float} freq\n * @param {Boolean} useSharps - (Optional) set to true to use sharps instead of flats\n * @return {String} note name\n * @example\n * freq.note(440) // => 'A4'\n */\nexport function note(freq, useSharps) {\n return midiToNote(toMidi(freq), useSharps);\n}\n\n/**\n * Get difference in cents between two frequencies. The frequencies can be\n * expressed with hertzs or midi numbers or note names\n * @param {Float|Integer|String} base\n * @param {Float|Integer|String} freq\n * @return {Integer} The difference in cents\n * @example\n * import { cents } from 'tonal-freq'\n * cents('C4', 261) // => -4\n */\nexport function cents(base, freq) {\n var b = toFreq(base) || base;\n var f = toFreq(freq) || freq;\n return Math.round(1200 * (Math.log(f / b) / Math.log(2)));\n}\n","/**\n * [![npm version](https://img.shields.io/npm/v/tonal-note.svg)](https://www.npmjs.com/package/tonal-note)\n * [![tonal](https://img.shields.io/badge/tonal-note-yellow.svg)](https://www.npmjs.com/browse/keyword/tonal)\n *\n * `tonal-note` is a collection of functions to manipulate musical notes in scientific notation\n *\n * This is part of [tonal](https://www.npmjs.com/package/tonal) music theory library.\n *\n * ## Usage\n *\n * ```js\n * import * as note from 'tonal-note'\n * // or var note = require('tonal-note')\n * note.name('bb2') // => 'Bb2'\n * note.chroma('bb2') // => 10\n * note.enharmonics('C#6') // => [ 'B##5', 'C#6', 'Db6' ]\n * note.simplify('B#3') // => 'C4'\n *\n * // using ES6 import syntax\n * import { name } from 'tonal-note'\n * ['c', 'db3', '2', 'g+', 'gx4'].map(name)\n * // => ['C', 'Db3', null, null, 'G##4']\n * ```\n *\n * ## Install\n *\n * [![npm install tonal-note](https://nodei.co/npm/tonal-note.png?mini=true)](https://npmjs.org/package/tonal-note/)\n *\n * ## API Documentation\n *\n * @module note\n */\nimport { build, parse } from \"note-parser\";\nimport { fifths, asNotePitch, strNote, parseIvl, decode } from \"tonal-pitch\";\nimport { transpose as tr } from \"tonal-transpose\";\nimport { toMidi, note as midiToNote } from \"tonal-midi\";\nimport { toFreq } from \"tonal-freq\";\n\nvar cache = {};\nfunction parseNote(name) {\n if (typeof name !== \"string\") return null;\n return cache[name] || (cache[name] = parse(name));\n}\n\n/**\n * Get the note midi number\n * (an alias of tonal-midi `toMidi` function)\n *\n * @function\n * @param {Array|String|Number} note - the note to get the midi number from\n * @return {Integer} the midi number or null if not valid pitch\n * @example\n * note.midi('C4') // => 60\n * @see midi.toMidi\n */\nexport var midi = toMidi;\n\n/**\n * Get the note name of a given midi note number\n * (an alias of tonal-midi `note` function)\n *\n * @function\n * @param {Integer} midi - the midi note number\n * @param {Boolean} useSharps - (Optional) set to true to use sharps instead of flats\n * @return {String} the note name\n * @example\n * note.fromMidi(60) // => 'C4'\n * @see midi.note\n */\nexport var fromMidi = midiToNote;\n\n/**\n * Get the frequency of a note\n * (an alias of the tonal-note package `toFreq` function)\n *\n * @function\n * @param {Array|String|Number} note - the note to get the frequency\n * @return {Number} the frequency\n * @example\n * note.freq('A4') // => 440\n * @see freq.toFreq\n */\nexport var freq = toFreq;\n\n/**\n * Return the chroma of a note. The chroma is the numeric equivalent to the\n * pitch class, where 0 is C, 1 is C# or Db, 2 is D... 11 is B\n *\n * @param {String|Pitch} note\n * @return {Integer} the chroma\n * @example\n * var note = require('tonal-note')\n * note.chroma('Cb') // => 11\n * ['C', 'D', 'E', 'F'].map(note.chroma) // => [0, 2, 4, 5]\n */\nexport function chroma(n) {\n var p = parseNote(n);\n return p ? p.chroma : null;\n}\n\n/**\n * Given a note (as string or as array notation) returns a string\n * with the note name in scientific notation or null\n * if not valid note\n *\n * Can be used to test if a string is a valid note name.\n *\n * @function\n * @param {Pitch|String}\n * @return {String}\n *\n * @example\n * var note = require('tonal-note')\n * note.name('cb2') // => 'Cb2'\n * ['c', 'db3', '2', 'g+', 'gx4'].map(note.name) // => ['C', 'Db3', null, null, 'G##4']\n */\nexport function name(n) {\n var p = asNotePitch(n);\n return p ? strNote(p) : null;\n}\n\n/**\n * @deprecated\n * An alias for note. Get the name of a note in scientific notation\n * @function\n */\nexport function note(n) {\n console.warn(\"note.note() is deprecated. Use note.name()\");\n return name(n);\n}\n\n/**\n * @deprecated\n * Get note properties. It returns an object with the following properties:\n *\n * - step: 0 for C, 6 for B. Do not confuse with chroma\n * - alt: 0 for not accidentals, positive sharps, negative flats\n * - oct: the octave number or undefined if a pitch class\n *\n * @param {String|Pitch} note - the note\n * @return {Object} the object with note properties or null if not valid note\n * @example\n * note.props('Db3') // => { step: 1, alt: -1, oct: 3 }\n * note.props('C#') // => { step: 0, alt: 1, oct: undefined }\n */\nexport function props(n) {\n console.warn(\n \"note.props() is deprecated. Use: note.step(), note.alt() or note.oct()\"\n );\n var p = asNotePitch(n);\n if (!p) return null;\n var d = decode(p);\n return { step: d[0], alt: d[1], oct: d[2] };\n}\n\n/**\n * @deprecated\n * Given a note properties object, return the string representation if\n * scientific notation\n *\n * @param {Object} noteProps - an object with the following attributes:\n * @return {String} the note name\n *\n * - step: a number from 0 to 6 meaning note step letter from 'C' to 'B'\n * - alt: the accidentals as number (0 no accidentals, 1 is '#', 2 is '##', -2 is 'bb')\n * - oct: (Optional) the octave. If not present (or undefined) it returns a pitch class\n *\n * @example\n * note.fromProps({ step: 1, alt: -1, oct: 5 }) // => 'Db5'\n * note.fromProps({ step: 0, alt: 1 }) // => 'C#'\n */\nexport function fromProps(props) {\n console.warn(\"note.fromProps() is deprecated. See npm package note-parser.\");\n return props ? build(props.step, props.alt, props.oct) : null;\n}\n\nfunction getProp(name) {\n return function(n) {\n var p = props(n);\n return p ? p[name] : null;\n };\n}\n\n/**\n * Get the octave of the given pitch\n *\n * @function\n * @param {String|Pitch} note - the note\n * @return {Integer} the octave, undefined if its a pitch class or null if\n * not a valid note\n * @example\n * note.oct('C#4') // => 4\n * note.oct('C') // => undefined\n * note.oct('blah') // => undefined\n */\nexport var oct = getProp(\"oct\");\n\n/**\n * Get the note step: a number equivalent of the note letter. 0 means C and\n * 6 means B. This is different from `chroma` (see example)\n *\n * @function\n * @param {String|Pitch} note - the note\n * @return {Integer} a number between 0 and 6 or null if not a note\n * @example\n * note.step('C') // => 0\n * note.step('Cb') // => 0\n * // usually what you need is chroma\n * note.chroma('Cb') // => 6\n */\nexport var step = getProp(\"step\");\n\n/**\n * @deprecated\n * Get the note step in fifths from 'C'. One property of the perfect fifth\n * interval is that you can obtain any pitch class by transposing 'C' a\n * number of times. This function return that number.\n * @param {String|Pitch} note - the note (can be a pitch class)\n * @return {Integer} the number of fifths to reach that pitch class from 'C'\n */\nexport function pcFifths(note) {\n var p = asNotePitch(note);\n return p ? fifths(p) : null;\n}\n\n/**\n * Get the note alteration: a number equivalent to the accidentals. 0 means\n * no accidentals, negative numbers are for flats, positive for sharps\n *\n * @function\n * @param {String|Pitch} note - the note\n * @return {Integer} the alteration\n * @example\n * note.alt('C') // => 0\n * note.alt('C#') // => 1\n * note.alt('Cb') // => -1\n */\nexport var alt = getProp(\"alt\");\n\n/**\n * Get pitch class of a note. The note can be a string or a pitch array.\n *\n * @function\n * @param {String|Pitch}\n * @return {String} the pitch class\n * @example\n * tonal.pc('Db3') // => 'Db'\n * tonal.map(tonal.pc, 'db3 bb6 fx2') // => [ 'Db', 'Bb', 'F##']\n */\nexport function pc(n) {\n var p = asNotePitch(n);\n return p ? strNote([p[0], [fifths(p)]]) : null;\n}\n\nvar ASC = parseIvl(\"2d\");\nvar DESC = parseIvl(\"-2d\");\n\n/**\n * Get the enharmonics of a note. It returns an array of three elements: the\n * below enharmonic, the note, and the upper enharmonic\n *\n * @param {String} note - the note to get the enharmonics from\n * @return {Array} an array of pitches ordered by distance to the given one\n *\n * @example\n * var note = require('tonal-note')\n * note.enharmonics('C') // => ['B#', 'C', 'Dbb']\n * note.enharmonics('A') // => ['G##', 'A', 'Bbb']\n * note.enharmonics('C#4') // => ['B##3', 'C#4' 'Db4']\n * note.enharmonics('Db') // => ['C#', 'Db', 'Ebbb'])\n */\nexport function enharmonics(pitch) {\n var notes = [];\n notes.push(tr(DESC, pitch));\n if (notes[0] === null) return null;\n notes.push(pitch);\n notes.push(tr(ASC, pitch));\n return notes;\n}\n\n/**\n * Get a simpler enharmonic note name from a note if exists\n *\n * @param {String} note - the note to simplify\n * @return {String} the simplfiied note (if not found, return same note)\n *\n * @example\n * var note = require('tonal-note')\n * note.simplify('B#3') // => 'C4'\n */\nexport function simplify(pitch) {\n return enharmonics(pitch).reduce(function(simple, next) {\n if (!simple) return next;\n return simple.length > next.length ? next : simple;\n }, null);\n}\n","// shorthand tonal notation (with quality after number)\nvar IVL_TNL = '([-+]?)(\\\\d+)(d{1,4}|m|M|P|A{1,4})'\n// standard shorthand notation (with quality before number)\nvar IVL_STR = '(AA|A|P|M|m|d|dd)([-+]?)(\\\\d+)'\nvar COMPOSE = '(?:(' + IVL_TNL + ')|(' + IVL_STR + '))'\nvar IVL_REGEX = new RegExp('^' + COMPOSE + '$')\n\n/**\n * Parse a string with an interval in shorthand notation (https://en.wikipedia.org/wiki/Interval_(music)#Shorthand_notation)\n * and returns an object with interval properties.\n *\n * @param {String} str - the string with the interval\n * @param {Boolean} strict - (Optional) if its false, it doesn't check if the\n * interval is valid or not. For example, parse('P2') returns null\n * (because a perfect second is not a valid interval), but\n * parse('P2', false) it returns { num: 2, dir: 1, q: 'P'... }\n * @return {Object} an object properties or null if not valid interval string\n * The returned object contains:\n * - `num`: the interval number\n * - `q`: the interval quality string (M is major, m is minor, P is perfect...)\n * - `simple`: the simplified number (from 1 to 7)\n * - `dir`: the interval direction (1 ascending, -1 descending)\n * - `type`: the interval type (P is perfectable, M is majorable)\n * - `alt`: the alteration, a numeric representation of the quality\n * - `oct`: the number of octaves the interval spans. 0 for simple intervals.\n * - `size`: the size of the interval in semitones\n * @example\n * var parse = require('interval-notation').parse\n * parse('M3')\n * // => { num: 3, q: 'M', dir: 1, simple: 3,\n * // type: 'M', alt: 0, oct: 0, size: 4 }\n */\nexport function parse (str, strict) {\n if (typeof str !== 'string') return null\n var m = IVL_REGEX.exec(str)\n if (!m) return null\n var i = { num: +(m[3] || m[8]), q: m[4] || m[6] }\n i.dir = (m[2] || m[7]) === '-' ? -1 : 1\n var step = (i.num - 1) % 7\n i.simple = step + 1\n i.type = TYPES[step]\n i.alt = qToAlt(i.type, i.q)\n i.oct = Math.floor((i.num - 1) / 7)\n i.size = i.dir * (SIZES[step] + i.alt + 12 * i.oct)\n if (strict !== false) {\n if (i.type === 'M' && i.q === 'P') return null\n }\n return i\n}\nvar SIZES = [0, 2, 4, 5, 7, 9, 11]\n\nvar TYPES = 'PMMPPMM'\n/**\n * Get the type of interval. Can be perfectavle ('P') or majorable ('M')\n * @param {Integer} num - the interval number\n * @return {String} `P` if it's perfectable, `M` if it's majorable.\n */\nexport function type (num) {\n return TYPES[(num - 1) % 7]\n}\n\nfunction dirStr (dir) { return dir === -1 ? '-' : '' }\nfunction num (simple, oct) { return simple + 7 * oct }\n\n/**\n * Build a shorthand interval notation string from properties.\n *\n * @param {Integer} simple - the interval simple number (from 1 to 7)\n * @param {Integer} alt - the quality expressed in numbers. 0 means perfect\n * or major, depending of the interval number.\n * @param {Integer} oct - the number of octaves the interval spans.\n * 0 por simple intervals. Positive number.\n * @param {Integer} dir - the interval direction: 1 ascending, -1 descending.\n * @example\n * var interval = require('interval-notation')\n * interval.shorthand(3, 0, 0, 1) // => 'M3'\n * interval.shorthand(3, -1, 0, -1) // => 'm-3'\n * interval.shorthand(3, 1, 1, 1) // => 'A10'\n */\nexport function shorthand (simple, alt, oct, dir) {\n return altToQ(simple, alt) + dirStr(dir) + num(simple, oct)\n}\n/**\n * Build a special shorthand interval notation string from properties.\n * The special shorthand interval notation changes the order or the standard\n * shorthand notation so instead of 'M-3' it returns '-3M'.\n *\n * The standard shorthand notation has a string 'A4' (augmented four) that can't\n * be differenciate from 'A4' (the A note in 4th octave), so the purpose of this\n * notation is avoid collisions\n *\n * @param {Integer} simple - the interval simple number (from 1 to 7)\n * @param {Integer} alt - the quality expressed in numbers. 0 means perfect\n * or major, depending of the interval number.\n * @param {Integer} oct - the number of octaves the interval spans.\n * 0 por simple intervals. Positive number.\n * @param {Integer} dir - the interval direction: 1 ascending, -1 descending.\n * @example\n * var interval = require('interval-notation')\n * interval.build(3, 0, 0, 1) // => '3M'\n * interval.build(3, -1, 0, -1) // => '-3m'\n * interval.build(3, 1, 1, 1) // => '10A'\n */\nexport function build (simple, alt, oct, dir) {\n return dirStr(dir) + num(simple, oct) + altToQ(simple, alt)\n}\n\n/**\n * Get an alteration number from an interval quality string.\n * It accepts the standard `dmMPA` but also sharps and flats.\n *\n * @param {Integer|String} num - the interval number or a string representing\n * the interval type ('P' or 'M')\n * @param {String} quality - the quality string\n * @return {Integer} the interval alteration\n * @example\n * qToAlt('M', 'm') // => -1 (for majorables, 'm' is -1)\n * qToAlt('P', 'A') // => 1 (for perfectables, 'A' means 1)\n * qToAlt('M', 'P') // => null (majorables can't be perfect)\n */\nexport function qToAlt (num, q) {\n var t = typeof num === 'number' ? type(num) : num\n if (q === 'M' && t === 'M') return 0\n if (q === 'P' && t === 'P') return 0\n if (q === 'm' && t === 'M') return -1\n if (/^A+$/.test(q)) return q.length\n if (/^d+$/.test(q)) return t === 'P' ? -q.length : -q.length - 1\n return null\n}\n\nfunction fillStr (s, n) { return Array(Math.abs(n) + 1).join(s) }\n/**\n * Get interval quality from interval type and alteration\n *\n * @function\n * @param {Integer|String} num - the interval number of the the interval\n * type ('M' for majorables, 'P' for perfectables)\n * @param {Integer} alt - the interval alteration\n * @return {String} the quality string\n * @example\n * altToQ('M', 0) // => 'M'\n */\nexport function altToQ (num, alt) {\n var t = typeof num === 'number' ? type(Math.abs(num)) : num\n if (alt === 0) return t === 'M' ? 'M' : 'P'\n else if (alt === -1 && t === 'M') return 'm'\n else if (alt > 0) return fillStr('A', alt)\n else if (alt < 0) return fillStr('d', t === 'P' ? alt : alt + 1)\n else return null\n}\n\n","/**\n * [![npm version](https://img.shields.io/npm/v/tonal-interval.svg)](https://www.npmjs.com/package/tonal-interval)\n * [![tonal](https://img.shields.io/badge/tonal-interval-yellow.svg)](https://www.npmjs.com/browse/keyword/tonal)\n *\n * `tonal-interval` is a collection of functions to create and manipulate music intervals.\n *\n * The intervals are strings in shorthand notation. Two variations are supported:\n *\n * - standard shorthand notation: type and number, for example: 'M3', 'd-4'\n * - inverse shorthand notation: number and then type, for example: '3M', '-4d'\n *\n * The problem with the standard shorthand notation is that some strings can be\n * parsed as notes or intervals, for example: 'A4' can be note A in 4th octave\n * or an augmented four. To remove ambiguity, the prefered notation in tonal is the\n * inverse shortand notation.\n *\n * This is part of [tonal](https://www.npmjs.com/package/tonal) music theory library.\n *\n * ## Usage\n *\n * ```js\n * import * as interval from 'tonal-interval'\n * // or var interval = require('tonal-interval')\n * interval.semitones('4P') // => 5\n * interval.invert('3m') // => '6M'\n * interval.simplify('9m') // => '2m'\n * ```\n *\n * ## Install\n *\n * [![npm install tonal-interval](https://nodei.co/npm/tonal-interval.png?mini=true)](https://npmjs.org/package/tonal-interval/)\n *\n * ## API Documentation\n *\n * @module interval\n */\nimport { build } from \"interval-notation\";\nimport {\n asIvlPitch,\n ivlFn,\n chr,\n dir,\n strIvl,\n encode,\n decode,\n height\n} from \"tonal-pitch\";\n\n/**\n * Get interval name. Can be used to test if it's an interval. It accepts intervals\n * as pitch or string in shorthand notation or tonal notation. It returns always\n * intervals in tonal notation.\n *\n * @param {String|Pitch} interval - the interval string or array\n * @return {String} the interval name or null if not valid interval\n * @example\n * interval.toInterval('m-3') // => '-3m'\n * interval.toInterval('3') // => null\n */\nexport function toInterval(ivl) {\n var i = asIvlPitch(ivl);\n return i ? strIvl(i) : null;\n}\n\n/**\n * Get the number of the interval (same as value, but always positive)\n *\n * @param {String|Pitch} interval - the interval\n * @return {Integer} the positive interval number (P1 is 1, m2 is 2, ...)\n * @example\n * interval.num('m2') // => 2\n * interval.num('P9') // => 9\n * interval.num('P-4') // => 4\n */\nexport function num(ivl) {\n var p = props(ivl);\n return p ? p.num : null;\n}\n\n/**\n * Get the interval value (the interval number, but positive or negative\n * depending the interval direction)\n *\n * @param {String|Pitch} interval - the interval\n * @return {Integer} the positive interval number (P1 is 1, m-2 is -2, ...)\n * @example\n * interval.num('m2') // => 2\n * interval.num('m9') // => 9\n * interval.num('P-4') // => -4\n * interval.num('m-9') // => -9\n */\nexport function value(ivl) {\n var p = props(ivl);\n return p ? p.num * p.dir : null;\n}\n\n/**\n * Get interval properties. It returns an object with:\n *\n * - num: the interval number (always positive)\n * - alt: the interval alteration (0 for perfect in perfectables, or 0 for major in _majorables_)\n * - dir: the interval direction (1 ascending, -1 descending)\n *\n * @param {String|Pitch} interval - the interval\n * @return {Array} the interval in the form [number, alt]\n * @example\n * interval.parse('m2') // => { num: 2, alt: -1, dir: 1 }\n * interval.parse('m9') // => { num: 9, alt: -1, dir: 1 }\n * interval.parse('P-4') // => { num: 4, alt: 0, dir: -1}\n * interval.parse('m-9') // => { num: 9, alt: -1, dir: -1 }\n */\nexport function props(ivl) {\n var i = asIvlPitch(ivl);\n if (!i) return null;\n var d = decode(i);\n return { num: d[0] + 1 + d[2] * 7, alt: d[1], dir: i[2] };\n}\n\n/**\n * Given a interval property object, get the interval name\n *\n * @param {Object} props - the interval property object\n *\n * - num: the interval number\n * - alt: the interval alteration\n * - dir: the direction\n * @return {String} the interval name\n */\nexport function fromProps(props) {\n if (!props || props.num < 1) return null;\n var octs = Math.floor(props.num / 8);\n var simple = props.num - 7 * octs;\n return build(simple, props.alt || 0, octs, props.dir);\n}\n\n/**\n * Get size in semitones of an interval\n * @param {String|Pitch} ivl\n * @return {Integer} the number of semitones or null if not an interval\n * @example\n * import { semitones } from 'tonal-interval'\n * semitones('P4') // => 5\n * // or using tonal\n * tonal.semitones('P5') // => 7\n */\nexport function semitones(ivl) {\n var i = asIvlPitch(ivl);\n return i ? height(i) : null;\n}\n\n// interval numbers\nvar IN = [1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7];\n// interval qualities\nvar IQ = \"P m M m M P d P m M m M\".split(\" \");\n\n/**\n * Get interval name from semitones number. Since there are several interval\n * names for the same number, the name it's arbitraty, but deterministic.\n * @param {Integer} num - the number of semitones (can be negative)\n * @return {String} the interval name\n * @example\n * import { fromSemitones } from 'tonal-interval'\n * fromSemitones(7) // => '5P'\n * // or using tonal\n * tonal.fromSemitones(-7) // => '-5P'\n */\nexport function fromSemitones(num) {\n var d = num < 0 ? -1 : 1;\n var n = Math.abs(num);\n var c = n % 12;\n var o = Math.floor(n / 12);\n return d * (IN[c] + 7 * o) + IQ[c];\n}\n\nvar CLASSES = [0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1];\n/**\n * Get the [interval class](https://en.wikipedia.org/wiki/Interval_class)\n * number of a given interval.\n *\n * In musical set theory, an interval class is the shortest distance in\n * pitch class space between two unordered pitch classes\n *\n * As paramter you can pass an interval in shorthand notation, an interval in\n * array notation or the number of semitones of the interval\n *\n * @param {String|Integer} interval - the interval or the number of semitones\n * @return {Integer} A value between 0 and 6\n *\n * @example\n * interval.ic('P8') // => 0\n * interval.ic('m6') // => 4\n * ['P1', 'M2', 'M3', 'P4', 'P5', 'M6', 'M7'].map(ic) // => [0, 2, 4, 5, 5, 3, 1]\n */\nexport function ic(ivl) {\n var i = asIvlPitch(ivl);\n var s = i ? chr(i) : Math.round(ivl);\n return isNaN(s) ? null : CLASSES[Math.abs(s) % 12];\n}\n\nvar TYPES = \"PMMPPMM\";\n/**\n * Get interval type. Can be perfectable (1, 4, 5) or majorable (2, 3, 6, 7)\n * It does NOT return the actual quality.\n *\n * @param {String|Pitch} interval\n * @return {String} 'P' for perfectables, 'M' for majorables or null if not\n * valid interval\n * @example\n * interval.type('5A') // => 'P'\n */\nexport function type(ivl) {\n var i = asIvlPitch(ivl);\n return i ? TYPES[decode(i)[0]] : null;\n}\n\n/**\n * Get the inversion (https://en.wikipedia.org/wiki/Inversion_(music)#Intervals)\n * of an interval.\n *\n * @function\n * @param {String|Pitch} interval - the interval to invert in interval shorthand\n * notation or interval array notation\n * @return {String|Pitch} the inverted interval\n *\n * @example\n * interval.invert('3m') // => '6M'\n * interval.invert('2M') // => '7m'\n */\nexport var invert = ivlFn(function(i) {\n var d = decode(i);\n // d = [step, alt, oct]\n var step = (7 - d[0]) % 7;\n var alt = TYPES[d[0]] === \"P\" ? -d[1] : -(d[1] + 1);\n return encode(step, alt, d[2], dir(i));\n});\n\n/**\n * Get the simplified version of an interval.\n *\n * @function\n * @param {String|Array} interval - the interval to simplify\n * @return {String|Array} the simplified interval\n *\n * @example\n * interval.simplify('9M') // => '2M'\n * ['8P', '9M', '10M', '11P', '12P', '13M', '14M', '15P'].map(interval.simplify)\n * // => [ '8P', '2M', '3M', '4P', '5P', '6M', '7M', '8P' ]\n * interval.simplify('2M') // => '2M'\n * interval.simplify('-2M') // => '7m'\n */\nexport var simplify = ivlFn(function(i) {\n // decode to [step, alt, octave]\n var dec = decode(i);\n // if it's not 8 reduce the octaves to 0\n if (dec[0] !== 0 || dec[2] !== 1) dec[2] = 0;\n // encode back\n return encode(dec[0], dec[1], dec[2], dir(i));\n});\n","/**\n * [![npm version](https://img.shields.io/npm/v/tonal-pcset.svg?style=flat-square)](https://www.npmjs.com/package/tonal-pcset)\n * [![tonal](https://img.shields.io/badge/tonal-pcset-yellow.svg?style=flat-square)](https://www.npmjs.com/browse/keyword/tonal)\n *\n * `tonal-pcset` is a collection of functions to work with pitch class sets, oriented\n * to make comparations (isEqual, isSubset, isSuperset)\n *\n * This is part of [tonal](https://www.npmjs.com/package/tonal) music theory library.\n *\n * You can install via npm: `npm i --save tonal-pcset`\n *\n * ```js\n * var pcset = require('tonal-pcset')\n * pcset.isEqual('c2 d5 e6', 'c6 e3 d1') // => true\n * ```\n *\n * ## API documentation\n *\n * @module pcset\n */\nimport { chr, asPitch } from \"tonal-pitch\";\nimport { pc } from \"tonal-note\";\nimport { map, asArr, rotate, compact } from \"tonal-array\";\nimport { transpose } from \"tonal-transpose\";\n\nfunction chrToInt(set) {\n return parseInt(chroma(set), 2);\n}\nfunction pitchChr(p) {\n p = asPitch(p);\n return p ? chr(p) : null;\n}\n\n/**\n * Get chroma of a pitch class set. A chroma identifies each set uniquely.\n * It's a 12-digit binary each presenting one semitone of the octave.\n *\n * Note that this function accepts a chroma as parameter and return it\n * without modification.\n *\n * @param {Array|String} set - the pitch class set\n * @return {String} a binary representation of the pitch class set\n * @example\n * pcset.chroma('C D E') // => '1010100000000'\n */\nexport function chroma(set) {\n if (isChroma(set)) return set;\n var b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n map(pitchChr, set).forEach(function(i) {\n b[i] = 1;\n });\n return b.join(\"\");\n}\n\n/**\n * @deprecated\n * @see collection.pcset\n * Given a list of notes, return the pitch class names of the set\n * starting with the first note of the list\n * @param {String|Array} notes - the pitch class set notes\n * @return {Array} an array of pitch class sets\n */\nexport function notes(notes) {\n // FIXME: move to collection\n console.warn(\"pcset.notes deprecated. Use collection.pcset\");\n var pcs = map(pc, notes);\n if (!pcs.length) return pcs;\n var tonic = pcs[0];\n // since the first note of the chroma is always C, we have to rotate it\n var rotated = rotate(pitchChr(tonic), chroma(pcs).split(\"\")).join(\"\");\n return fromChroma(rotated, tonic);\n}\n\n/**\n * Given a a list of notes or a pcset chroma, produce the rotations\n * of the chroma discarding the ones that starts with '0'\n *\n * This is used, for example, to get all the modes of a scale.\n *\n * @param {Array|String} set - the list of notes or pitchChr of the set\n * @param {Boolean} normalize - (Optional, true by default) remove all\n * the rotations that starts with '0'\n * @return {Array} an array with all the modes of the chroma\n *\n * @example\n * pcset.modes('C E G')\n */\nexport function modes(set, normalize) {\n normalize = normalize !== false;\n var binary = chroma(set).split(\"\");\n return compact(\n binary.map(function(_, i) {\n var r = rotate(i, binary);\n return normalize && r[0] === \"0\" ? null : r.join(\"\");\n })\n );\n}\n/**\n * @deprecated\n * @see modes\n */\nexport function chromaModes(set, norm) {\n console.warn(\"pcset.chromaModes deprecated. Renamed to pcset.modes\");\n return modes(set, norm);\n}\n\nvar REGEX = /^[01]{12}$/;\n\n/**\n * Test if the given string is a pitch class set chroma.\n * @param {String} chroma - the pitch class set chroma\n * @return {Boolean} true if its a valid pcset chroma\n * @example\n * pcset.isChroma('101010101010') // => true\n * pcset.isChroma('101001') // => false\n */\nexport function isChroma(set) {\n return REGEX.test(set);\n}\n\nvar IVLS = \"1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M\".split(\" \");\n/**\n * Given a pcset (notes or chroma) return it's intervals\n * @param {String|Array} pcset - the pitch class set (notes or chroma)\n * @return {Array} intervals or empty array if not valid pcset\n * @example\n * pcset.intervals('1010100000000') => ['C', 'D', 'E']\n */\nexport function intervals(set) {\n return compact(\n chroma(set)\n .split(\"\")\n .map(function(d, i) {\n return d === \"1\" ? IVLS[i] : null;\n })\n );\n}\n\n/**\n * @deprecated\n * @see intervals\n * Given a pitch class set in binary notation it returns the intervals or notes\n * (depending on the tonic)\n * @param {String} binary - the pitch class set in binary representation\n * @param {String|Pitch} tonic - the pitch class set tonic\n * @return {Array} a list of notes or intervals\n * @example\n * pcset.fromChroma('101010101010', 'C') // => ['C', 'D', 'E', 'Gb', 'Ab', 'Bb']\n */\nexport function fromChroma(binary, tonic) {\n console.warn(\n \"pcset.fromChroma is deprecated. Use pcset.intervals().map(...)\"\n );\n if (arguments.length === 1)\n return function(t) {\n return fromChroma(binary, t);\n };\n if (!tonic) tonic = \"P1\";\n return intervals(binary).map(transpose(tonic));\n}\n\n/**\n * Test if two pitch class sets are identical\n *\n * @param {Array|String} set1 - one of the pitch class sets\n * @param {Array|String} set2 - the other pitch class set\n * @return {Boolean} true if they are equal\n * @example\n * pcset.isEqual('c2 d3', 'c5 d2') // => true\n */\nexport function isEqual(s1, s2) {\n if (arguments.length === 1)\n return function(s) {\n return isEqual(s1, s);\n };\n return chroma(s1) === chroma(s2);\n}\nexport function equal(a, b) {\n console.warn(\"pcset.equal is deprecated. Use pcset.isEqual\");\n return isEqual(a, b);\n}\n\n/**\n * Test if a pitch class set is a subset of another\n *\n * @param {Array|String} set - the base set to test against\n * @param {Array|String} test - the set to test\n * @return {Boolean} true if the test set is a subset of the set\n * @example\n * pcset.subset('c d e', 'C2 D4 D5 C6') // => true\n */\nexport function isSubset(set, test) {\n if (arguments.length === 1)\n return function(t) {\n return isSubset(set, t);\n };\n test = chrToInt(test);\n return (test & chrToInt(set)) === test;\n}\nexport function subset(a, b) {\n console.warn(\"pcset.subset is deprecated. Use pcset.isSubset\");\n return isSubset(a, b);\n}\n\n/**\n * Test if a pitch class set is a superset\n *\n * @param {Array|String} set - the base set to test against\n * @param {Array|String} test - the set to test\n * @return {Boolean} true if the test set is a superset of the set\n * @example\n * pcset.isSuperset('c d e', 'C2 D4 F4 D5 E5 C6') // => true\n */\nexport function isSuperset(set, test) {\n if (arguments.length === 1)\n return function(t) {\n return isSuperset(set, t);\n };\n test = chrToInt(test);\n return (test | chrToInt(set)) === test;\n}\nexport function superset(a, b) {\n console.warn(\"pcset.superset is deprecated. Use pcset.isSuperset\");\n return isSuperset(a, b);\n}\n\n/**\n * Test if a given pitch class set includes a note\n * @param {Array|String} set - the base set to test against\n * @param {String|Pitch} note - the note to test\n * @return {Boolean} true if the note is included in the pcset\n * @example\n * pcset.includes('c d e', 'C4') // =A true\n * pcset.includes('c d e', 'C#4') // =A false\n */\nexport function includes(set, note) {\n if (arguments.length > 1) return includes(set)(note);\n set = chroma(set);\n return function(note) {\n return set[pitchChr(note)] === \"1\";\n };\n}\n\n/**\n * Filter a list with a pitch class set\n *\n * @param {Array|String} set - the pitch class set notes\n * @param {Array|String} notes - the note list to be filtered\n * @return {Array} the filtered notes\n *\n * @example\n * pcset.filter('c d e', 'c2 c#2 d2 c3 c#3 d3') // => [ 'c2', 'd2', 'c3', 'd3' ])\n * pcset.filter('c2', 'c2 c#2 d2 c3 c#3 d3') // => [ 'c2', 'c3' ])\n */\nexport function filter(set, notes) {\n if (arguments.length === 1)\n return function(n) {\n return filter(set, n);\n };\n return asArr(notes).filter(includes(set));\n}\n","/**\n * A collection of functions to create note ranges.\n *\n * @example\n * var range = require('tonal-range')\n * // ascending chromatic range\n * range.chromatic(['C4', 'E4']) // => ['C4', 'Db4', 'D4', 'Eb4', 'E4']\n * // descending chromatic range\n * range.chromatic(['E4', 'C4']) // => ['E4', 'Eb4', 'D4', 'Db4', 'C4']\n * // combining ascending and descending in complex ranges\n * range.chromatic(['C2', 'E2', 'D2']) // => ['C2', 'Db2', 'D2', 'Eb2', 'E2', 'Eb2', 'D2']\n * // numeric (midi note numbers) range\n * range.numeric('C4 E4 Bb3') // => [60, 61, 62, 63, 64]\n * // complex numeric range\n * range.numeric('C4 E4 Bb3') // => [60, 61, 62, 63, 64, 63, 62, 61, 60, 59, 58]\n * // create a scale range\n * range.pitchSet('c e g a', 'c2 c3 c2') // => [ 'C2', 'E2', 'G2', 'A2', 'C3', 'A2', 'G2', 'E2', 'C2' ] *\n g\n * @module range\n */\nimport { asArr, map } from \"tonal-array\";\nimport { trFifths } from \"tonal-transpose\";\nimport { toMidi, note } from \"tonal-midi\";\nimport { filter } from \"tonal-pcset\";\n\nfunction isNum(n) {\n return typeof n === \"number\";\n}\n// convert notes to midi if needed\nfunction asNum(n) {\n return isNum(n) ? n : toMidi(n);\n}\n// ascending range\nfunction ascR(b, n) {\n for (var a = []; n--; a[n] = n + b);\n return a;\n}\n// descending range\nfunction descR(b, n) {\n for (var a = []; n--; a[n] = b - n);\n return a;\n}\n// create a range between a and b\nfunction ran(a, b) {\n return a === null || b === null\n ? []\n : a < b ? ascR(a, b - a + 1) : descR(a, a - b + 1);\n}\n\n/**\n * Create a numeric range. You supply a list of notes or numbers and it will\n * be conected to create complex ranges.\n *\n * @param {String|Array} list - the list of notes or numbers used\n * @return {Array} an array of numbers or empty array if not vald parameters\n *\n * @example\n * range.numeric([\"C5\", \"C4']) // => [ 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60 ]\n * // it works midi notes\n * range.numeric([10, 5]) // => [ 10, 9, 8, 7, 6, 5 ]\n * // complex range\n * range.numeric('C4 E4 Bb3') // => [60, 61, 62, 63, 64, 63, 62, 61, 60, 59, 58]\n * // can be expressed with a string or array\n * range.numeric('C2 C4 C2') === range.numeric(['C2', 'C4', 'C2'])\n */\nexport function numeric(list) {\n return asArr(list)\n .map(asNum)\n .reduce(function(r, n, i) {\n if (i === 1) return ran(r, n);\n var last = r[r.length - 1];\n return r.concat(ran(last, n).slice(1));\n });\n}\n\n/**\n * Create a range of chromatic notes. The altered notes will use flats.\n *\n * @function\n * @param {String|Array} list - the list of notes or midi note numbers\n * @return {Array} an array of note names\n * @example\n * tonal.chromatic('C2 E2 D2') // => ['C2', 'Db2', 'D2', 'Eb2', 'E2', 'Eb2', 'D2']\n * // with sharps\n * tonal.chromatic('C2 C3', true) // => [ 'C2', 'C#2', 'D2', 'D#2', 'E2', 'F2', 'F#2', 'G2', 'G#2', 'A2', 'A#2', 'B2', 'C3' ]\n */\nexport function chromatic(list, sharps) {\n return map(note(sharps === true), numeric(list));\n}\n\n/**\n * Create a range with a cycle of fifths\n * @function\n * @param {String|Pitch} tonic - the tonic note or pitch class\n * @param {Array|String} range - the range array\n * @return {Array} a range of cycle of fifths starting with the tonic\n * @example\n * range.fifths('C', [0, 6]) // => [ 'C', 'G', 'D', 'A', 'E', 'B', 'F#' ])\n */\nexport function fifths(tonic, range) {\n return numeric(range).map(trFifths(tonic));\n}\n\n/**\n * Create a pitch set (scale or chord) range. Given a pitch set (a collection\n * of pitch classes), and a range array, it returns a range in notes.\n *\n * @param {String|Array|Function} scale - the scale to use or a function to\n * convert from midi numbers to note names\n * @param {String|Array} range - a list of notes or midi numbers\n * @return {Array} the scale range, an empty array if not valid source or\n * null if not valid start or end\n * @example\n * range.pitchSet('C D E F G A B', ['C3', 'C2'])\n * // => [ 'C3', 'B2', 'A2', 'G2', 'F2', 'E2', 'D2', 'C2' ]\n */\nexport function pitchSet(set, range) {\n if (arguments.length === 1)\n return function(l) {\n return pitchSet(set, l);\n };\n\n return filter(set, chromatic(range));\n}\n","/**\n * Functions related to music notation in strings. Things like parse accidentals,\n * or convert from step to note letter.\n *\n * Glossary:\n *\n * - step: the number from 0 to 6 representing the letters from C to B\n * - letter: a valid note letter (from A to G)\n * - alteration: a number indicating the sharps (positive) or flats (negative)\n * - accidentals: a string with sharps (#) or flats (b)\n *\n * @example\n * var notation = require('tonal-notation')\n * notation.toAcc('3') // => '###'\n * notation.toAcc('-3') // => 'bbb'\n * notation.toAlt('###') // => 3\n * @module notation\n */\n\n/**\n * Given a letter, return step\n * @param {String} letter - the letter\n * @return {Integer} the step number (from 0 to 6)\n */\nexport function toStep(l) {\n var s = \"CDEFGAB\".indexOf(l.toUpperCase());\n return s < 0 ? null : s;\n}\n\n/**\n * Test if a number is a valid step number (a number from 0 to 6)\n * @param {Integer} step - the step number\n * @return {Boolean} true if it's a valid step number, false otherwise\n */\nexport function isStep(d) {\n return !(d < 0 || d > 6);\n}\n\n/**\n * Given a step, return a letter\n * @param {Integer} step - the step number\n * @return {String} the note letter or null if not valid step number\n */\nexport function toLetter(s) {\n return isStep(s) ? \"CDEFGAB\".charAt(s) : null;\n}\n\n// ACCIDENTALS\n// ===========\n\n/**\n * Test if a string are all flats (`b`) chars\n * @param {String} str - the string to test\n * @return {Boolean} true if all charaters are `b`, false otherwise\n */\nexport function areFlats(s) {\n return /^b+$/.test(s);\n}\n/**\n * Test if a string are all sharps (`#`) chars\n * @param {String} str - the string to test\n * @return {Boolean} true if all charaters are `#`, false otherwise\n */\nexport function areSharps(s) {\n return /^#+$/.test(s);\n}\n\n/**\n * Given an accidentals string return its alteration, the number\n * of semitones (positive for sharps, negative for flats, 0 for none)\n * @param {String} accidentals - the string to parse\n * @return {Integer} the alteration number of null if not a valid accidental strings\n * @example\n * toAlt('###') // => 3\n * toAlt('bbb') // => -3\n */\nexport function toAlt(s) {\n return s === \"\"\n ? 0\n : areFlats(s) ? -s.length : areSharps(s) ? s.length : null;\n}\n\nfunction fillStr(s, num) {\n return Array(num + 1).join(s);\n}\n\n/**\n * Given an alteration number, returns the accidentals string\n * @param {Integer} alteration - the number of semitones (positive and negative\n * values are accepted for sharps and flats)\n * @return {String} the accidental string\n * @example\n * toAcc(3) // => '###'\n * toAcc(-3) // => 'bbb'\n */\nexport function toAcc(n) {\n return !n ? \"\" : n < 0 ? fillStr(\"b\", -n) : fillStr(\"#\", n);\n}\n","/**\n * _Key_ refers to the tonal system based on the major and minor scales. This is\n * is the most common tonal system, but tonality can be present in music\n * based in other scales or concepts.\n *\n * This is a collection of functions related to keys.\n *\n * @example\n * var key = require('tonal-key')\n * key.scale('E mixolydian') // => [ 'E', 'F#', 'G#', 'A', 'B', 'C#', 'D' ]\n * key.relative('minor', 'C major') // => 'A minor'\n *\n * @module key\n */\n\nimport { areFlats, areSharps, toAcc } from \"tonal-notation\";\nimport { trFifths } from \"tonal-transpose\";\nimport { pc, pcFifths } from \"tonal-note\";\nimport { numeric } from \"tonal-range\";\nimport { rotate } from \"tonal-array\";\nimport { harmonics, harmonize } from \"tonal-harmonizer\";\n\n// Order matters: use an array\nvar MODES = [\n \"ionian\",\n \"dorian\",\n \"phrygian\",\n \"lydian\",\n \"mixolydian\",\n \"aeolian\",\n \"locrian\",\n \"major\",\n \"minor\"\n];\n// { C: 0, D: 2, E: 4, F: -1, G: 1, A: 3, B: 5 }\nvar FIFTHS = [0, 2, 4, -1, 1, 3, 5, 0, 3];\nvar SCALES = [0, 1, 2, 3, 4, 5, 6, 0, 5].map(function(n) {\n return harmonics(rotate(n, [\"C\", \"D\", \"E\", \"F\", \"G\", \"A\", \"B\"]));\n});\n\n// PRIVATE\n// Given a tonic, mode pair, return the key string\nfunction toKey(t, m) {\n return !t ? m : t + \" \" + m;\n}\n// Given the alterations, return the major key\nfunction majorKey(n) {\n return toKey(trFifths(\"C\", n), \"major\");\n}\n// given the mode name, return the alterations\nfunction modeNum(mode) {\n return FIFTHS[MODES.indexOf(mode)];\n}\n// given a string, return the valid mode it represents or null\nfunction validMode(m) {\n m = m.trim().toLowerCase();\n return MODES.indexOf(m) === -1 ? null : m;\n}\n\n/**\n * Return the key properties, an object with { tonic, mode }\n *\n * @param {String} name - the key name\n * @return {Key} the key properties object or null if not a valid key\n * @example\n * var key = require('tonal-key')\n * key.props('C3 dorian') // => { tonic: 'C', mode: 'dorian' }\n * key.props('dorian') // => { tonic: false, mode: 'dorian' }\n * key.props('Ab bebop') // => null\n * key.props('blah') // => null\n */\nexport function props(str) {\n if (typeof str !== \"string\") return null;\n var ndx = str.indexOf(\" \");\n var key;\n if (ndx === -1) {\n var p = pc(str);\n key = p\n ? { tonic: p, mode: \"major\" }\n : { tonic: false, mode: validMode(str) };\n } else {\n key = { tonic: pc(str.slice(0, ndx)), mode: validMode(str.slice(ndx + 1)) };\n }\n return key.mode ? key : null;\n}\n\n/**\n * Test if a given name is a valid key name\n *\n * @param {String} name\n * @param {Boolean}\n * @example\n * key.isKeyName('C major') // => true\n * key.isKeyName('major') // => true\n * key.isKeyName('Bb bebop') // => false\n */\nexport function isKeyName(name) {\n return props(name) !== null;\n}\n\n/**\n * Get the tonic of a key\n *\n * @param {String} key - the key\n * @return {String} the tonic or false is no tonic, or null if its not a valid key\n * @example\n * key.tonic('c3 major') // => 'C'\n * key.tonic('minor') // => false\n * key.tonic('bebop') // null\n */\nexport function tonic(key) {\n return (props(key) || key || {}).tonic || null;\n}\n\n/**\n * Get the mode of a key. It can be used to test if its a valid key mode.\n *\n * @param {String}\n * @return {Boolean}\n * @example\n * key.mode('A dorian') // => 'dorian'\n * key.mode('DORIAN') // => 'dorian'\n * key.mode('mixophrygian') // => null\n */\nexport function mode(key) {\n return (props(key) || key || {}).mode || null;\n}\n\n/**\n * Get relative of a key. Two keys are relative when the have the same\n * key signature (for example C major and A minor)\n *\n * It can be partially applied.\n *\n * @param {String} mode - the relative destination\n * @param {String} key - the key source\n * @example\n * key.relative('dorian', 'B major') // => 'C# dorian'\n * // partial application\n * var minor = key.relative('minor')\n * minor('C major') // => 'A minor'\n * minor('E major') // => 'C# minor'\n */\nexport function relative(rel, key) {\n if (arguments.length === 1)\n return function(k) {\n return relative(rel, k);\n };\n rel = props(rel);\n if (!rel || rel.tonic) return null;\n key = props(key);\n if (!key || !key.tonic) return null;\n var tonic = trFifths(key.tonic, modeNum(rel.mode) - modeNum(key.mode));\n return toKey(tonic, rel.mode);\n}\n\n/**\n * Get a list of the altered notes of a given key. The notes will be in\n * the same order than in the key signature.\n * @param {String|Nunber} key\n * @return {Array}\n * @example\n * var key = require('tonal-keys')\n * key.alteredNotes('Eb major') // => [ 'Bb', 'Eb', 'Ab' ]\n */\nexport function alteredNotes(key) {\n var alt = alteration(key);\n return alt === null\n ? null\n : alt < 0\n ? numeric([-1, alt]).map(trFifths(\"F\"))\n : numeric([1, alt]).map(trFifths(\"B\"));\n}\n\n/**\n * Get a list of valid mode names. The list of modes will be always in\n * increasing order (ionian to locrian)\n *\n * @param {Boolean} alias - true to get aliases names\n * @return {Array} an array of strings\n * @example\n * key.modes() // => [ 'ionian', 'dorian', 'phrygian', 'lydian',\n * // 'mixolydian', 'aeolian', 'locrian' ]\n * key.modes(true) // => [ 'ionian', 'dorian', 'phrygian', 'lydian',\n * // 'mixolydian', 'aeolian', 'locrian', 'major', 'minor' ]\n */\nexport function modes(alias) {\n return alias ? MODES.slice() : MODES.slice(0, -2);\n}\n\n/**\n * Create a major key from alterations\n * @function\n * @param {Integer} alt - the alteration number (positive sharps, negative flats)\n * @return {Key} the key object\n * @example\n * var key = require('tonal-key')\n * key.fromAlter(2) // => 'D major'\n */\nexport function fromAlter(n) {\n return typeof n === \"number\" ? majorKey(n) : null;\n}\n\n/**\n * Get key name from accidentals\n *\n * @param {String} acc - the accidentals string\n * @return {Key} the key object\n * @example\n * var key = require('tonal-key')\n * key.fromAcc('b') // => 'F major'\n * key.fromAcc('##') // => 'D major'\n */\nexport function fromAcc(s) {\n return areSharps(s)\n ? majorKey(s.length)\n : areFlats(s) ? majorKey(-s.length) : null;\n}\n\n/**\n * Get scale of a key\n *\n * @param {String|Object} key\n * @return {Array} the key scale\n * @example\n * key.scale('A major') // => [ 'A', 'B', 'C#', 'D', 'E', 'F#', 'G#' ]\n * key.scale('Bb minor') // => [ 'Bb', 'C', 'Db', 'Eb', 'F', 'Gb', 'Ab' ]\n * key.scale('C dorian') // => [ 'C', 'D', 'Eb', 'F', 'G', 'A', 'Bb' ]\n * key.scale('E mixolydian') // => [ 'E', 'F#', 'G#', 'A', 'B', 'C#', 'D' ]\n */\nexport function scale(key) {\n var p = props(key);\n if (!p || !p.tonic) return null;\n return harmonize(SCALES[MODES.indexOf(p.mode)], p.tonic);\n}\n\n/**\n * Get key alteration. The alteration is a number indicating the number of\n * sharpen notes (positive) or flaten notes (negative)\n * @param {String|Integer} key\n * @return {Integer}\n * @example\n * var key = require('tonal-keys')\n * key.alteration('A major') // => 3\n */\nexport function alteration(key) {\n var k = props(key);\n if (!k || !k.tonic) return null;\n var toMajor = modeNum(k.mode);\n var toC = pcFifths(k.tonic);\n return toC - toMajor;\n}\n\n/**\n * Get the signature of a key. The signature is a string with sharps or flats.\n * @example\n * var key = require('tonal-keys')\n * key.signature('A major') // => '###'\n */\nexport function signature(key) {\n return toAcc(alteration(key));\n}\n\n/**\n * An alias for `signature()`\n * @function\n */\nexport var accidentals = signature;\n","import { map, compact, sort } from \"tonal-array\";\nimport { pc } from \"tonal-note\";\nimport { chroma, modes } from \"tonal-pcset\";\n\n/**\n * This module contains functions to query tonal dictionaries.\n *\n * A tonal dictionary is basically a map from keys to list of intervals. It\n * also supports name aliases. See `tonal-chords` or `tonal-scales` to examples\n * of dictionaries.\n *\n * This functions are quite low level, and probably you wont need it, because\n * they are friendly served via `tonal-chords` and `tonal-scales`.\n *\n * __Those functions are NOT visible via `tonal` package__.\n *\n * @module dictionary\n */\nfunction id(x) {\n return x;\n}\n\n/**\n * Create a tonal dictionary. A dictionary is an object with two functions: get and\n * keys.\n *\n * The data given to this constructor it's a HashMap in the form:\n * `{ key: [intervals, [aliases]] }`\n *\n * @param {HashMap} data - the dictionary data\n * @return {Object} the dictionary object\n *\n * @example\n * var dictionary = require('tonal-dictionary').dictionary\n * var DATA = {\n * 'maj7': ['1 3 5 7', ['Maj7']],\n * 'm7': ['1 b3 5 7']\n * }\n * var chords = dictionary(DATA, function (str) { return str.split(' ') })\n * chords.get('maj7') // => [ '1', '3', '5', '7' ]\n * chords.get('Maj7') // => [ '1', '3', '5', '7' ]\n * chords.get('m7') // => ['1', 'b3', '5', '7']\n * chords.get('m7b5') // => null\n * chords.keys() // => ['maj7', 'm7']\n * chords.keys(true) // => ['maj7', 'm7', 'Maj7']\n */\nexport function dictionary(raw, parse) {\n parse = parse || id;\n var byKey = {};\n var names = Object.keys(raw);\n var aliases = [];\n names.forEach(function(k) {\n var value = parse(raw[k][0]);\n byKey[k] = value;\n if (raw[k][1]) {\n raw[k][1].forEach(function(alias) {\n byKey[alias] = value;\n aliases.push(alias);\n });\n }\n });\n return {\n /**\n * Get a value by key\n * @name get\n * @function\n * @param {String} key\n * @return {Object} the value (normally an array of intervals or notes)\n * @memberof dictionary\n */\n get: function(n) {\n return byKey[n];\n },\n /**\n * Get the valid keys of dictionary\n * @name keys\n * @function\n * @param {Boolean} aliases - (Optional) include aliases names (false by default)\n * @param {Function} filter - a function to filter the names. It receives the\n * name and the value as parameters\n * @return {Array} the keys\n * @memberof dictionary\n */\n keys: function(all, filter) {\n var keys = all ? names.concat(aliases) : names.slice();\n return typeof filter !== \"function\"\n ? keys\n : keys.filter(function(k) {\n return filter(k, byKey[k]);\n });\n }\n };\n}\n\n/**\n * Create a pitch set detector. Given a dictionary data, it returns a\n * function that tries to detect a given pitch set inside the dictionary\n *\n * @param {Dictionary} dictionary - the dictionary object\n * @param {Function|String} builder - (Optional) a function that given a name and a tonic,\n * returns the object or a string to join both\n * @return {Function} the detector function\n * @see chord.detect\n * @see scale.detect\n * @example\n * var detect = detector(dictionary(DATA), '')\n * detect('c d e b') // => 'Cmaj/'\n */\nexport function detector(dict, build) {\n var isSep = typeof build === \"string\";\n var isFn = typeof build === \"function\";\n var nameByChroma = dict.keys(false).reduce(function(map, key) {\n map[chroma(dict.get(key))] = key;\n return map;\n }, {});\n\n return function(notes) {\n notes = sort(map(pc, notes));\n var sets = modes(notes);\n return compact(\n sets.map(function(set, i) {\n var type = nameByChroma[set];\n if (!type) return null;\n var tonic = notes[i];\n return isSep\n ? tonic + build + type\n : isFn ? build(type, tonic) : [type, tonic];\n })\n );\n };\n}\n","/**\n * A scale is a collection of pitches in ascending or descending order.\n *\n * This module provides functions to get and manipulate scales.\n *\n * @example\n * scale.notes('Ab bebop') // => [ 'Ab', 'Bb', 'C', 'Db', 'Eb', 'F', 'Gb', 'G' ]\n * scale.get('hungarian major', 'B3') // => [ 'B3', 'C##4', 'D#4', 'E#4', 'F#4', 'G#4', 'A4'\n * scale.get('C E F G', 'F') // => [ 'F', 'A', 'Bb', 'C' ]\n * scale.get('1P 2M 3M 5P 6M', 'D4') // => [ 'D4', 'E4', 'F#4', 'A4', 'B4' ]\n * scale.names() => ['major', 'minor', ...]\n * scale.detect('f5 d2 c5 b5 a2 e4 g') // => [ 'C major', 'D dorian', 'E phrygian', 'F lydian', 'G mixolydian', 'A aeolian', 'B locrian'])\n * @module scale\n */\nimport { dictionary, detector } from \"tonal-dictionary\";\nimport { map, compact } from \"tonal-array\";\nimport { pc, name as note } from \"tonal-note\";\nimport { harmonize } from \"tonal-harmonizer\";\nimport DATA from \"./scales.json\";\n\nvar dict = dictionary(DATA, function(str) {\n return str.split(\" \");\n});\n\n/**\n * Transpose the given scale notes, intervals or name to a given tonic.\n * The returned scale is an array of notes (or intervals if you specify `false` as tonic)\n *\n * It returns null if the scale type is not in the scale dictionary\n *\n * This function is currified\n *\n * @param {String} source - the scale type, intervals or notes\n * @param {String} tonic - the scale tonic (or false to get intervals)\n * @return {Array} the scale notes\n *\n * @example\n * scale.get('bebop', 'Eb') // => [ 'Eb', 'F', 'G', 'Ab', 'Bb', 'C', 'Db', 'D' ]\n * scale.get('major', false) // => [ '1P', '2M', '3M', '4P', '5P', '6M', '7M' ]\n * var major = scale.get('major')\n * major('Db3') // => [ 'Db3', 'Eb3', 'F3', 'Gb3', 'Ab3', 'Bb3', 'C4' ]\n */\nexport function get(type, tonic) {\n if (arguments.length === 1)\n return function(t) {\n return get(type, t);\n };\n var ivls = dict.get(type);\n return ivls ? harmonize(ivls, tonic) : null;\n}\n\n/**\n * Return the available scale names\n *\n * @function\n * @param {boolean} aliases - true to include aliases\n * @return {Array} the scale names\n *\n * @example\n * var scale = require('tonal-scale')\n * scale.names() // => ['maj7', ...]\n */\nexport var names = dict.keys;\n\n/**\n * Get the notes (pitch classes) of a scale. It accepts either a scale name\n * (tonic and type) or a collection of notes.\n *\n * Note that it always returns an array, and the values are only pitch classes.\n *\n * @param {String|Array} src - the scale name (it must include the scale type and\n * a tonic. The tonic can be a note or a pitch class) or the list of notes\n * @return {Array} the scale pitch classes\n *\n * @example\n * scale.notes('C major') // => [ 'C', 'D', 'E', 'F', 'G', 'A', 'B' ]\n * scale.notes('C4 major') // => [ 'C', 'D', 'E', 'F', 'G', 'A', 'B' ]\n * scale.notes('Ab bebop') // => [ 'Ab', 'Bb', 'C', 'Db', 'Eb', 'F', 'Gb', 'G' ]\n * scale.notes('C4 D6 E2 c7 a2 b5 g2 g4 f') // => ['C', 'D', 'E', 'F', 'G', 'A', 'B']\n */\nexport function notes(name) {\n var scale = parse(name);\n var notes = scale.tonic ? get(scale.type, pc(scale.tonic)) : null;\n return (\n notes ||\n compact(\n map(pc, name).map(function(n, i, arr) {\n // check for duplicates\n // TODO: sort but preserving the root\n return arr.indexOf(n) < i ? null : n;\n })\n )\n );\n}\n\n/**\n * Given a scale name, return its intervals. The name can be the type and\n * optionally the tonic (which is ignored)\n *\n * It retruns an empty array when no scale found\n *\n * @param {String} name - the scale name (tonic and type, tonic is optional)\n * @return {Array} the scale intervals if is a known scale or an empty\n * array if no scale found\n * @example\n * scale.intervals('C major')\n */\nexport function intervals(name) {\n var scale = parse(name);\n return get(scale.type, false) || [];\n}\n\n/**\n * Check if the given name (and optional tonic and type) is a know scale\n * @param {String} name - the scale name\n * @return {Boolean}\n * @example\n * scale.intervals('C major') // => [ '1P', '2M', '3M', '4P', '5P', '6M', '7M' ])\n * scale.intervals('major') // => [ '1P', '2M', '3M', '4P', '5P', '6M', '7M' ])\n * scale.intervals('mixophrygian') // => null\n */\nexport function isKnowScale(name) {\n return intervals(name).length > 0;\n}\n\n/**\n * Given a string try to parse as scale name. It retuns an object with the\n * form { tonic, type } where tonic is the note or false if no tonic specified\n * and type is the rest of the string minus the tonic\n *\n * Note that this function doesn't check that the scale type is a valid scale\n * type or if is present in any scale dictionary.\n *\n * @param {String} name - the scale name\n * @return {Object} an object { tonic, type }\n * @example\n * scale.parse('C mixoblydean') // => { tonic: 'C', type: 'mixoblydean' }\n * scale.parse('anything is valid') // => { tonic: false, type: 'anything is valid'}\n */\nexport function parse(str) {\n if (typeof str !== \"string\") return null;\n var i = str.indexOf(\" \");\n var tonic = note(str.substring(0, i)) || false;\n var type = tonic ? str.substring(i + 1) : str;\n return { tonic: tonic, type: type };\n}\n\n/**\n * Detect a scale. Given a list of notes, return the scale name(s) if any.\n * It only detects chords with exactly same notes.\n *\n * @function\n * @param {Array|String} notes - the list of notes\n * @return {Array} an array with the possible scales\n * @example\n * scale.detect('b g f# d') // => [ 'GMaj7' ]\n * scale.detect('e c a g') // => [ 'CM6', 'Am7' ]\n */\nexport var detect = detector(dict, \" \");\n","'use strict'\n\n// util\nfunction fillStr (s, num) { return Array(num + 1).join(s) }\nfunction isNum (x) { return typeof x === 'number' }\nfunction isStr (x) { return typeof x === 'string' }\nfunction isDef (x) { return typeof x !== 'undefined' }\nfunction midiToFreq (midi, tuning) {\n return Math.pow(2, (midi - 69) / 12) * (tuning || 440)\n}\n\nvar REGEX = /^([a-gA-G])(#{1,}|b{1,}|x{1,}|)(-?\\d*)\\s*(.*)\\s*$/\n/**\n * A regex for matching note strings in scientific notation.\n *\n * @name regex\n * @function\n * @return {RegExp} the regexp used to parse the note name\n *\n * The note string should have the form `letter[accidentals][octave][element]`\n * where:\n *\n * - letter: (Required) is a letter from A to G either upper or lower case\n * - accidentals: (Optional) can be one or more `b` (flats), `#` (sharps) or `x` (double sharps).\n * They can NOT be mixed.\n * - octave: (Optional) a positive or negative integer\n * - element: (Optional) additionally anything after the duration is considered to\n * be the element name (for example: 'C2 dorian')\n *\n * The executed regex contains (by array index):\n *\n * - 0: the complete string\n * - 1: the note letter\n * - 2: the optional accidentals\n * - 3: the optional octave\n * - 4: the rest of the string (trimmed)\n *\n * @example\n * var parser = require('note-parser')\n * parser.regex.exec('c#4')\n * // => ['c#4', 'c', '#', '4', '']\n * parser.regex.exec('c#4 major')\n * // => ['c#4major', 'c', '#', '4', 'major']\n * parser.regex().exec('CMaj7')\n * // => ['CMaj7', 'C', '', '', 'Maj7']\n */\nexport function regex () { return REGEX }\n\nvar SEMITONES = [0, 2, 4, 5, 7, 9, 11]\n/**\n * Parse a note name in scientific notation an return it's components,\n * and some numeric properties including midi number and frequency.\n *\n * @name parse\n * @function\n * @param {String} note - the note string to be parsed\n * @param {Boolean} isTonic - true the strings it's supposed to contain a note number\n * and some category (for example an scale: 'C# major'). It's false by default,\n * but when true, en extra tonicOf property is returned with the category ('major')\n * @param {Float} tunning - The frequency of A4 note to calculate frequencies.\n * By default it 440.\n * @return {Object} the parsed note name or null if not a valid note\n *\n * The parsed note name object will ALWAYS contains:\n * - letter: the uppercase letter of the note\n * - acc: the accidentals of the note (only sharps or flats)\n * - pc: the pitch class (letter + acc)\n * - step: s a numeric representation of the letter. It's an integer from 0 to 6\n * where 0 = C, 1 = D ... 6 = B\n * - alt: a numeric representation of the accidentals. 0 means no alteration,\n * positive numbers are for sharps and negative for flats\n * - chroma: a numeric representation of the pitch class. It's like midi for\n * pitch classes. 0 = C, 1 = C#, 2 = D ... 11 = B. Can be used to find enharmonics\n * since, for example, chroma of 'Cb' and 'B' are both 11\n *\n * If the note has octave, the parser object will contain:\n * - oct: the octave number (as integer)\n * - midi: the midi number\n * - freq: the frequency (using tuning parameter as base)\n *\n * If the parameter `isTonic` is set to true, the parsed object will contain:\n * - tonicOf: the rest of the string that follows note name (left and right trimmed)\n *\n * @example\n * var parse = require('note-parser').parse\n * parse('Cb4')\n * // => { letter: 'C', acc: 'b', pc: 'Cb', step: 0, alt: -1, chroma: -1,\n * oct: 4, midi: 59, freq: 246.94165062806206 }\n * // if no octave, no midi, no freq\n * parse('fx')\n * // => { letter: 'F', acc: '##', pc: 'F##', step: 3, alt: 2, chroma: 7 })\n */\nexport function parse (str, isTonic, tuning) {\n if (typeof str !== 'string') return null\n var m = REGEX.exec(str)\n if (!m || (!isTonic && m[4])) return null\n\n var p = { letter: m[1].toUpperCase(), acc: m[2].replace(/x/g, '##') }\n p.pc = p.letter + p.acc\n p.step = (p.letter.charCodeAt(0) + 3) % 7\n p.alt = p.acc[0] === 'b' ? -p.acc.length : p.acc.length\n var pos = SEMITONES[p.step] + p.alt\n p.chroma = pos < 0 ? 12 + pos : pos % 12\n if (m[3]) { // has octave\n p.oct = +m[3]\n p.midi = pos + 12 * (p.oct + 1)\n p.freq = midiToFreq(p.midi, tuning)\n }\n if (isTonic) p.tonicOf = m[4]\n return p\n}\n\nvar LETTERS = 'CDEFGAB'\nfunction accStr (n) { return !isNum(n) ? '' : n < 0 ? fillStr('b', -n) : fillStr('#', n) }\nfunction octStr (n) { return !isNum(n) ? '' : '' + n }\n\n/**\n * Create a string from a parsed object or `step, alteration, octave` parameters\n * @param {Object} obj - the parsed data object\n * @return {String} a note string or null if not valid parameters\n * @since 1.2\n * @example\n * parser.build(parser.parse('cb2')) // => 'Cb2'\n *\n * @example\n * // it accepts (step, alteration, octave) parameters:\n * parser.build(3) // => 'F'\n * parser.build(3, -1) // => 'Fb'\n * parser.build(3, -1, 4) // => 'Fb4'\n */\nexport function build (s, a, o) {\n if (s === null || typeof s === 'undefined') return null\n if (s.step) return build(s.step, s.alt, s.oct)\n if (s < 0 || s > 6) return null\n return LETTERS.charAt(s) + accStr(a) + octStr(o)\n}\n\n/**\n * Get midi of a note\n *\n * @name midi\n * @function\n * @param {String|Integer} note - the note name or midi number\n * @return {Integer} the midi number of the note or null if not a valid note\n * or the note does NOT contains octave\n * @example\n * var parser = require('note-parser')\n * parser.midi('A4') // => 69\n * parser.midi('A') // => null\n * @example\n * // midi numbers are bypassed (even as strings)\n * parser.midi(60) // => 60\n * parser.midi('60') // => 60\n */\nexport function midi (note) {\n if ((isNum(note) || isStr(note)) && note >= 0 && note < 128) return +note\n var p = parse(note)\n return p && isDef(p.midi) ? p.midi : null\n}\n\n/**\n * Get freq of a note in hertzs (in a well tempered 440Hz A4)\n *\n * @name freq\n * @function\n * @param {String} note - the note name or note midi number\n * @param {String} tuning - (Optional) the A4 frequency (440 by default)\n * @return {Float} the freq of the number if hertzs or null if not valid note\n * @example\n * var parser = require('note-parser')\n * parser.freq('A4') // => 440\n * parser.freq('A') // => null\n * @example\n * // can change tuning (440 by default)\n * parser.freq('A4', 444) // => 444\n * parser.freq('A3', 444) // => 222\n * @example\n * // it accepts midi numbers (as numbers and as strings)\n * parser.freq(69) // => 440\n * parser.freq('69', 442) // => 442\n */\nexport function freq (note, tuning) {\n var m = midi(note)\n return m === null ? null : midiToFreq(m, tuning)\n}\n\nexport function letter (src) { return (parse(src) || {}).letter }\nexport function acc (src) { return (parse(src) || {}).acc }\nexport function pc (src) { return (parse(src) || {}).pc }\nexport function step (src) { return (parse(src) || {}).step }\nexport function alt (src) { return (parse(src) || {}).alt }\nexport function chroma (src) { return (parse(src) || {}).chroma }\nexport function oct (src) { return (parse(src) || {}).oct }\n","/**\n * A chord is a harmonic unit with at least three different tones sounding simultaneously.\n *\n * This module have functions to create and manipulate chords. It includes a\n * chord dictionary and a simple chord detection algorithm.\n *\n * @example\n * var chord = require('tonal-chord')\n * chord.detect('c b g e') // => 'CMaj7'\n * chord.get('CMaj7') // => ['C', 'E', 'G', 'B']\n *\n * @module chord\n */\nimport { dictionary, detector } from \"tonal-dictionary\";\nimport { map, compact, permutations, rotate } from \"tonal-array\";\nimport { pc, name as note } from \"tonal-note\";\nimport { regex } from \"note-parser\";\nimport { harmonize, intervallic } from \"tonal-harmonizer\";\nimport DATA from \"./chords.json\";\n\nvar dict = dictionary(DATA, function(str) {\n return str.split(\" \");\n});\n\n/**\n * Return the available chord names\n *\n * @function\n * @param {boolean} aliases - true to include aliases\n * @return {Array} the chord names\n *\n * @example\n * var chord = require('tonal-chord')\n * chord.names() // => ['maj7', ...]\n */\nexport var names = dict.keys;\n\n/**\n * Get chord notes or intervals from chord type\n *\n * This function is currified\n *\n * @param {String} type - the chord type\n * @param {Strng|Pitch} tonic - the tonic or false to get the intervals\n * @return {Array} the chord notes or intervals, or null if not valid type\n *\n * @example\n * chords.get('dom7', 'C') // => ['C', 'E', 'G', 'Bb']\n * maj7 = chords.get('Maj7')\n * maj7('C') // => ['C', 'E', 'G', 'B']\n */\nexport function get(type, tonic) {\n if (arguments.length === 1)\n return function(t) {\n return get(type, t);\n };\n var ivls = dict.get(type);\n return ivls ? harmonize(ivls, tonic) : null;\n}\n\n/**\n * Get the chord notes of a chord. This function accepts either a chord name\n * (for example: 'Cmaj7') or a list of notes.\n *\n * It always returns an array, even if the chord is not found.\n *\n * @param {String|Array} chord - the chord to get the notes from\n * @return {Array} a list of notes or empty list if not chord found\n *\n * @example\n * chord.notes('Cmaj7') // => ['C', 'E', 'G', 'B']\n */\nexport function notes(chord) {\n var p = parse(chord);\n var ivls = dict.get(p.type);\n return ivls ? harmonize(ivls, p.tonic) : compact(map(note, chord));\n}\n\n/**\n * Get chord intervals. It always returns an array\n *\n * @param {String} name - the chord name (optionally a tonic and type)\n * @return {Array} a list of intervals or null if the type is not known\n */\nexport function intervals(name) {\n var p = parse(name);\n return dict.get(p.type) || [];\n}\n\n/**\n * Check if a given name correspond to a chord in the dictionary\n * @param {String} name\n * @return {Boolean}\n * @example\n * chord.isKnownChord('CMaj7') // => true\n * chord.isKnownChord('Maj7') // => true\n * chord.isKnownChord('Ablah') // => false\n */\nexport function isKnownChord(name) {\n return intervals(name).length > 0;\n}\n\n/**\n * Detect a chord. Given a list of notes, return the chord name(s) if any.\n * It only detects chords with exactly same notes.\n *\n * @function\n * @param {Array|String} notes - the list of notes\n * @return {Array} an array with the possible chords\n * @example\n * chord.detect('b g f# d') // => [ 'GMaj7' ]\n * chord.detect('e c a g') // => [ 'CM6', 'Am7' ]\n */\nexport var detect = detector(dict, \"\");\n\n/**\n * Get the position (inversion number) of a chord (0 is root position, 1 is first\n * inversion...). It assumes the chord is formed by superposed thirds.\n *\n * @param {Array|String} chord - the chord notes\n * @return {Integer} the inversion number (0 for root inversion, 1 for first\n * inversion...) or null if not a valid chord\n *\n * @example\n * chord.position('e g c') // => 1\n * chord.position('g3 e2 c5') // => 1 (e is the lowest note)\n */\nexport function position(chord) {\n var pcs = map(pc, chord);\n var sorted = sortTriads(pcs);\n return sorted ? sorted.indexOf(pcs[0]) : null;\n}\n\n/**\n * Given a chord in any inverstion, set to the given inversion. It accepts\n * chord names\n *\n * @param {Integer} num - the inversion number (0 root position, 1 first\n * inversion, ...)\n * @param {String|Array} chord - the chord name or notes\n * @return {Array} the chord pitch classes in the desired inversion or\n * an empty array if no inversion found (not triadic)\n *\n * @example\n * chord.inversion(1, 'Cmaj7') // => [ 'E', 'G', 'B', 'C' ]\n * chord.inversion(0, 'e g c') // => [ 'C', 'E', 'G' ]\n */\nexport function inversion(num, chord) {\n if (arguments.length === 1)\n return function(c) {\n return inversion(num, c);\n };\n var sorted = sortTriads(chord);\n return sorted ? rotate(num, sorted) : [];\n}\n\nfunction sortTriads(chord) {\n var all = permutations(notes(chord).map(pc));\n for (var i = 0; i < all.length; i++) {\n var ivls = intervallic(all[i]);\n if (areTriads(ivls)) return all[i];\n }\n return null;\n}\n\nfunction areTriads(list) {\n for (var i = 0; i < list.length; i++) {\n if (list[i][0] !== \"3\") return false;\n }\n return true;\n}\n\n/**\n * Try to parse a chord name. It returns an array with the chord type and\n * the tonic. If not tonic is found, all the name is considered the chord\n * name.\n *\n * This function does NOT check if the chord type exists or not. It only tries\n * to split the tonic and chord type.\n *\n * @param {String} name - the chord name\n * @return {Array} an array with [type, tonic]\n * @example\n * chord.parse('Cmaj7') // => { tonic: 'C', type: 'maj7' }\n * chord.parse('C7') // => { tonic: 'C', type: '7' }\n * chord.parse('mMaj7') // => { tonic: false, type: 'mMaj7' }\n * chord.parse('Cnonsense') // => { tonic: 'C', type: 'nonsense' }\n */\nexport function parse(name) {\n var p = regex().exec(name);\n if (!p) return { type: name, tonic: false };\n\n // If chord name is empty, the octave is the chord name\n return !p[4]\n ? { type: p[3], tonic: p[1] + p[2] }\n : // If the octave is 6 or 7 is asumed to be part of the chord name\n p[3] === \"7\" || p[3] === \"6\"\n ? { type: p[3] + p[4], tonic: p[1] + p[2] }\n : { type: p[4], tonic: p[1] + p[2] + p[3] };\n}\n","/**\n * # `tonal-progressions`\n * > Describe and manipulate chord progressions.\n *\n * @example\n * var progression = require('tonal-progression')\n * progression.abstract('Cmaj7 Dm7 G7', 'C')\n *\n * @module progression\n */\nimport { pc } from \"tonal-note\";\nimport { props, fromProps } from \"tonal-interval\";\nimport { map, compact } from \"tonal-array\";\nimport { transpose } from \"tonal-transpose\";\nimport { interval } from \"tonal-distance\";\nimport { parse } from \"tonal-chord\";\nimport { toAcc } from \"tonal-notation\";\n\n/**\n * Given a chord progression and a tonic, return the chord progression\n * with roman numeral chords.\n *\n * @param {Array|String} chords - the chord progression\n * @param {String} tonic - the tonic\n * @return {Array} the chord progression in roman numerals\n * @example\n * progression.abstract('Cmaj7 Dm7 G7', 'C') // => [ 'Imaj7', 'IIm7', 'V7' ]\n */\nexport function abstract(chords, tonic) {\n tonic = pc(tonic);\n chords = map(parse, chords);\n var tonics = compact(\n chords.map(function(x) {\n return x.tonic;\n })\n );\n // if some tonic missing, can't do the analysis\n if (tonics.length !== chords.length) return null;\n\n return tonics.map(function(t, i) {\n var p = props(interval(tonic, t));\n return buildRoman(p.num - 1, p.alt, chords[i].type);\n });\n}\n\nvar NUMS = [\"I\", \"II\", \"III\", \"IV\", \"V\", \"VI\", \"VII\"];\n/**\n * Build an abstract chord name using roman numerals\n */\nexport function buildRoman(num, alt, element) {\n return toAcc(alt) + NUMS[num % 7] + (element || \"\");\n}\n\n/**\n * Get chord progression from a tonic and a list of chord in roman numerals\n *\n * @param {String} tonic - the tonic\n * @param {Array|String} progression - the progression in roman numerals\n * @return {Array} the chord progression\n *\n * @example\n * var progression = require('chord-progression')\n * progression.concrete('I IIm7 V7', 'C') // => ['C', 'Dm7', 'G7']\n */\nexport function concrete(chords, tonic) {\n return map(function(e) {\n var r = parseRomanChord(e);\n return r ? transpose(r.root, tonic) + r.type : null;\n }, chords);\n}\n\nvar ROMAN = /^\\s*(b|bb|#|##|)(IV|III|II|I|VII|VI|V|iv|iii|ii|i|vii|vi|v)\\s*(.*)\\s*$/;\n/**\n * Returns a regex to match roman numbers literals with the from:\n * `[accidentals]roman[element]`.\n *\n * The executed regex contains:\n *\n * - input: the input string\n * - accidentals: (Optional) one or two flats (b) or shaprs (#)\n * - roman: (Required) a roman numeral from I to VII either in upper or lower case\n * - element: (Optional) a name of an element\n *\n * @return {RegExp} the regexp\n *\n * @example\n * var r = progression.romanRegex()\n * r.exec('bVImaj7') // => ['bVImaj7', 'b', 'VI', 'maj7'])\n * r.exec('III dom') // => ['III dom', '', 'III', 'dom'])\n */\nexport function romanRegex() {\n return ROMAN;\n}\n\nvar NUM = { i: 0, ii: 1, iii: 2, iv: 3, v: 4, vi: 5, vii: 6 };\n\n/**\n * Parse a chord expressed with roman numerals. It returns an interval representing\n * the root of the chord relative to the key tonic and the chord name.\n *\n * @param {String} str - the roman numeral string\n * @return {Object} the roman chord property object with:\n *\n * - type: the chord type\n * - root: the interval from the key to the root of this chord\n *\n * @example\n * var parse = require('music-notation/roman.parse')\n * parse('V7') // => { root: '5P', type: '7' }\n * parse('bIIalt') // => { root: '2m', type: 'alt' }\n */\nexport function parseRomanChord(str) {\n var m = ROMAN.exec(str);\n if (!m) return null;\n var num = NUM[m[2].toLowerCase()] + 1;\n var alt = m[1].length;\n if (m[1][0] === \"b\") alt = -alt;\n return { root: fromProps({ num: num, alt: alt, dir: 1 }), type: m[3] };\n}\n","/**\n *\n * @module sonority\n */\nimport { ic } from \"tonal-interval\";\nimport { asNotePitch, chr } from \"tonal-pitch\";\nimport { map, compact } from \"tonal-array\";\n\n/**\n * Get the intervals analysis of a collection of notes\n *\n * Returns an array with the format `[p, m, n, s, d, t]` where:\n *\n * - p: the number of perfect fourths or fifths\n * - m: the number of major thirds or minor sixths\n * - n: the number of major sixths or minor thirds\n * - s: the number of major seconds or minor sevenths\n * - d: the number of major sevents or minor seconds\n * - t: the number of tritones\n *\n * This is, mostly, an academic puzzle to show the expresiveness of tonal.\n * Implements the ideas found in \"The Analysis of Intervals\" chapter from\n * [Harmonic Materials of Modern Music]():\n *\n * > The letters _pmn_, therefore, represent intervals commonly considered\n * consonant, whereas the letters _sdt_ represent the intervals commonly\n * considered dissonant. (...) A sonority represented, for example, by the\n * symbol `sd^2`, indicating a triad composed of one major second and two minor\n * seconds, would be recognized as a highly dissonant sound, while the symbol\n * `pmn` would indicate a consonant sound.\n *\n * @param {Array|String} notes - the notes to analyze\n * @return {Array} the _pmnsdt_ array\n */\nexport function density(list) {\n var a, b, i;\n var notes = compact(map(asNotePitch, list));\n var len = notes.length;\n var result = [0, 0, 0, 0, 0, 0];\n for (a = 0; a < len; a++) {\n for (b = a; b < len; b++) {\n i = ic(chr(notes[b]) - chr(notes[a]));\n if (i === 6) result[5] = result[5] + 1;\n else if (i > 0) result[5 - i] = result[5 - i] + 1;\n }\n }\n return result;\n}\n","/**\n * Functions to create and manipulate pitch sets\n *\n * @example\n * var pitchset = require('tonal-pitchset')\n *\n * @module pitchset\n */\nimport { sort } from \"tonal-array\";\n\n/**\n * Get the notes of a pitch set. The notes in the set are sorted in asceding\n * pitch order, and no repetitions are allowed.\n *\n * Note that it creates pitch sets and NOT picth class sets. This functionallity\n * resides inside `tonal-pcset` module.\n *\n * @param {String|Array} notes - the notes to create the pitch set from\n * @return {Array} the ordered pitch set notes\n * @example\n * pitchset.notes('C4 c3 C5 c4') // => ['C3', 'C4', 'C5']\n */\nexport function notes(notes) {\n return sort(notes).filter(function(n, i, arr) {\n return i === 0 || n !== arr[i - 1];\n });\n}\n","/**\n * The `tonal` module is a facade to all the rest of the modules. They are namespaced,\n * so for example to use `pc` function from `tonal-note` you have to write:\n * `tonal.note.pc`\n *\n * Some modules are NOT namespaced for developer comfort:\n *\n * - `tonal-array`: for example `tonal.map(tonal.note.pc, 'C#2')`\n * - `tonal-transpose`: for example `tonal.transpose('C', '3M')`\n * - `tonal-distance`: for example `tonal.interval('C3', 'G4')`\n *\n * It also adds a couple of function aliases:\n *\n * - `tonal.scale` is an alias for `tonal.scale.notes`\n * - `tonal.chord` is an alias for `tonal.chord.notes`\n *\n * @example\n * var tonal = require('tonal')\n * tonal.transpose(tonal.note.pc('C#2'), 'M3') // => 'E#'\n * tonal.chord('Dmaj7') // => ['D', 'F#', 'A', 'C#']\n *\n * @module tonal\n */\nimport * as array from \"tonal-array\";\nimport * as transpose from \"tonal-transpose\";\nimport * as harmonizer from \"tonal-harmonizer\";\nimport * as distance from \"tonal-distance\";\nimport * as note from \"tonal-note\";\nimport * as interval from \"tonal-interval\";\nimport * as midi from \"tonal-midi\";\nimport * as freq from \"tonal-freq\";\nimport * as range from \"tonal-range\";\nimport * as key from \"tonal-key\";\nimport * as scale from \"tonal-scale\";\nimport * as chord from \"tonal-chord\";\nimport * as pitch from \"tonal-pitch\";\nimport * as notation from \"tonal-notation\";\nimport * as progression from \"tonal-progression\";\nimport * as sonority from \"tonal-sonority\";\nimport * as pitchset from \"tonal-pitchset\";\nimport * as pcset from \"tonal-pcset\";\n\nvar assign = Object.assign;\nvar tonal = assign({}, array, transpose, harmonizer, distance);\ntonal.pitch = pitch;\ntonal.notation = notation;\ntonal.note = note;\ntonal.ivl = interval;\ntonal.midi = midi;\ntonal.freq = freq;\ntonal.range = range;\ntonal.key = key;\ntonal.progression = progression;\ntonal.sonority = sonority;\ntonal.pitchset = pitchset;\ntonal.pcset = pcset;\n\ntonal.scale = function(name) {\n return tonal.scale.notes(name);\n};\nassign(tonal.scale, scale);\ntonal.chord = function(name) {\n return tonal.chord.notes(name);\n};\nassign(tonal.chord, chord);\n\nif (typeof window !== \"undefined\") window.Tonal = tonal;\nexport default tonal;\n"],"names":["fillStr","s","num","Array","join","isNum","x","midiToFreq","midi","tuning","Math","pow","parse","str","isTonic","m","REGEX","exec","p","letter","toUpperCase","acc","replace","pc","step","charCodeAt","alt","length","pos","SEMITONES","chroma","oct","freq","tonicOf","accStr","n","octStr","build","a","o","LETTERS","charAt","strict","IVL_REGEX","i","q","dir","simple","type","TYPES","qToAlt","floor","size","SIZES","t","test","abs","altToQ","encode","f","FIFTHS","FIFTH_OCTS","unaltered","decode","STEPS","pitch","fifths","focts","isPitch","isArray","enc","dec","apply","pType","isNotePitch","isIvlPitch","isPC","height","chr","memoize","fn","cache","parsePitch","parseNote","parseIvl","asNotePitch","asIvlPitch","asPitch","strNote","noteStr","strIvl","d","strPitch","decorator","is","v","trBy","transpose","b","arguments","pa","pb","r","trFifths","substr","interval","semitones","hasVal","e","map","list","l","asArr","compact","arr","filter","objHeight","Infinity","ascComp","descComp","sort","comp","slice","listFn","trOct","tr","rotate","times","len","concat","select","nums","permutations","reduce","perm","newPerm","splice","asPitchStr","listToStr","isArr","harmonics","intervallic","notes","dist","push","harmonize","tonic","isStr","isDef","note","toMidi","val","sharps","round","SHARPS","FLATS","eqTempFreq","ref","max","noteToMidi","eqTempFreqToMidi","log","name","props","console","warn","getProp","pcFifths","enharmonics","DESC","ASC","dirStr","ivl","fromProps","octs","ic","isNaN","CLASSES","chrToInt","set","parseInt","pitchChr","isChroma","forEach","modes","normalize","binary","split","_","intervals","IVLS","fromChroma","isEqual","s1","s2","isSubset","isSuperset","includes","asNum","ascR","descR","ran","numeric","last","chromatic","pitchSet","range","isStep","areFlats","areSharps","toAcc","toKey","majorKey","modeNum","mode","MODES","indexOf","validMode","trim","toLowerCase","key","ndx","relative","rel","k","alteration","toMajor","signature","id","dictionary","raw","byKey","names","Object","keys","aliases","value","alias","get","all","detector","dict","isSep","isFn","nameByChroma","ivls","substring","regex","chord","inversion","c","sorted","sortTriads","areTriads","buildRoman","element","NUMS","parseRomanChord","ROMAN","NUM","root","RegExp","noteParse","ivlParse","noteFn","ivlFn","pitchFn","sep","undefined","shuffle","random","head","tail","toFreq","useSharps","midiToNote","base","fromMidi","next","IN","IQ","invert","simplify","pcs","norm","SCALES","accidentals","detect","scale","ii","iii","iv","vi","vii","chords","tonics","result","assign","tonal","array","harmonizer","distance","notation","progression","sonority","pitchset","pcset","window","Tonal"],"mappings":"iLAGA,SAASA,EAASC,EAAGC,GAAO,OAAOC,MAAMD,EAAM,GAAGE,KAAKH,GACvD,SAASI,EAAOC,GAAK,MAAoB,iBAANA,EACnC,SAESC,EAAYC,EAAMC,GACzB,OAAOC,KAAKC,IAAI,GAAIH,EAAO,IAAM,KAAOC,GAAU,KAoFpD,SAAgBG,EAAOC,EAAKC,EAASL,GACnC,GAAmB,iBAARI,EAAkB,OAAO,KACpC,IAAIE,EAAIC,GAAMC,KAAKJ,GACnB,IAAKE,IAAOD,GAAWC,EAAE,GAAK,OAAO,KAErC,IAAIG,GAAMC,OAAQJ,EAAE,GAAGK,cAAeC,IAAKN,EAAE,GAAGO,QAAQ,KAAM,OAC9DJ,EAAEK,GAAKL,EAAEC,OAASD,EAAEG,IACpBH,EAAEM,MAAQN,EAAEC,OAAOM,WAAW,GAAK,GAAK,EACxCP,EAAEQ,IAAmB,MAAbR,EAAEG,IAAI,IAAcH,EAAEG,IAAIM,OAAST,EAAEG,IAAIM,OACjD,IAAIC,EAAMC,GAAUX,EAAEM,MAAQN,EAAEQ,IAQhC,OAPAR,EAAEY,OAASF,EAAM,EAAI,GAAKA,EAAMA,EAAM,GAClCb,EAAE,KACJG,EAAEa,KAAOhB,EAAE,GACXG,EAAEV,KAAOoB,EAAM,IAAMV,EAAEa,IAAM,GAC7Bb,EAAEc,KAAOzB,EAAWW,EAAEV,KAAMC,IAE1BK,IAASI,EAAEe,QAAUlB,EAAE,IACpBG,EAIT,SAASgB,EAAQC,GAAK,OAAQ9B,EAAM8B,GAAUA,EAAI,EAAInC,EAAQ,KAAMmC,GAAKnC,EAAQ,IAAKmC,GAA7C,GACzC,SAASC,EAAQD,GAAK,OAAQ9B,EAAM8B,GAAU,GAAKA,EAAV,GAgBzC,SAAgBE,EAAOpC,EAAGqC,EAAGC,GAC3B,OAAU,OAANtC,QAA2B,IAANA,EAA0B,KAC/CA,EAAEuB,KAAaa,EAAMpC,EAAEuB,KAAMvB,EAAEyB,IAAKzB,EAAE8B,KACtC9B,EAAI,GAAKA,EAAI,EAAU,KACpBuC,GAAQC,OAAOxC,GAAKiC,EAAOI,GAAKF,EAAOG,GCtGhD,SAAgB3B,EAAOC,EAAK6B,GAC1B,GAAmB,iBAAR7B,EAAkB,OAAO,KACpC,IAAIE,EAAI4B,GAAU1B,KAAKJ,GACvB,IAAKE,EAAG,OAAO,KACf,IAAI6B,GAAM1C,MAAOa,EAAE,IAAMA,EAAE,IAAK8B,EAAG9B,EAAE,IAAMA,EAAE,IAC7C6B,EAAEE,IAAyB,OAAlB/B,EAAE,IAAMA,EAAE,KAAe,EAAI,EACtC,IAAIS,GAAQoB,EAAE1C,IAAM,GAAK,EAMzB,OALA0C,EAAEG,OAASvB,EAAO,EAClBoB,EAAEI,KAAOC,GAAMzB,GACfoB,EAAElB,IAAMwB,EAAON,EAAEI,KAAMJ,EAAEC,GACzBD,EAAEb,IAAMrB,KAAKyC,OAAOP,EAAE1C,IAAM,GAAK,GACjC0C,EAAEQ,KAAOR,EAAEE,KAAOO,GAAM7B,GAAQoB,EAAElB,IAAM,GAAKkB,EAAEb,MAChC,IAAXW,GACa,MAAXE,EAAEI,MAAwB,MAARJ,EAAEC,EAAkB,KAErCD,EAUT,SAAgBI,EAAM9C,GACpB,OAAO+C,IAAO/C,EAAM,GAAK,GA8D3B,SAAgBgD,EAAQhD,EAAK2C,GAC3B,IAAIS,EAAmB,iBAARpD,EAAmB8C,EAAK9C,GAAOA,EAC9C,MAAU,MAAN2C,GAAmB,MAANS,EAAkB,EACzB,MAANT,GAAmB,MAANS,EAAkB,EACzB,MAANT,GAAmB,MAANS,GAAmB,EAChC,OAAOC,KAAKV,GAAWA,EAAElB,OACzB,OAAO4B,KAAKV,GAAiB,MAANS,GAAaT,EAAElB,QAAUkB,EAAElB,OAAS,EACxD,KAGT,SAAS3B,EAASC,EAAGkC,GAAK,OAAOhC,MAAMO,KAAK8C,IAAIrB,GAAK,GAAG/B,KAAKH,GAY7D,SAAgBwD,EAAQvD,EAAKwB,GAC3B,IAAI4B,EAAmB,iBAARpD,EAAmB8C,EAAKtC,KAAK8C,IAAItD,IAAQA,EACxD,OAAY,IAARwB,EAAwB,MAAN4B,EAAY,IAAM,KACtB,IAAT5B,GAAoB,MAAN4B,EAAkB,IAChC5B,EAAM,EAAU1B,EAAQ,IAAK0B,GAC7BA,EAAM,EAAU1B,EAAQ,IAAW,MAANsD,EAAY5B,EAAMA,EAAM,GAClD,KC5Id,SAASrB,EAAM8B,GACb,MAAoB,iBAANA,EAqBhB,SAAgBuB,EAAOlC,EAAME,EAAKK,GAChC,IAAI4B,EAAIC,GAAOpC,GAAQ,EAAIE,EAC3B,OAAKrB,EAAM0B,IAEH4B,EADA5B,EAAM8B,GAAWrC,GAAQ,EAAIE,IADZiC,GAM3B,SAASG,EAAUH,GACjB,IAAIf,GAAKe,EAAI,GAAK,EAClB,OAAOf,EAAI,EAAI,EAAIA,EAAIA,EAezB,SAAgBmB,EAAOJ,EAAGpB,GACxB,IAAIf,EAAOwC,GAAMF,EAAUH,IACvBjC,EAAMhB,KAAKyC,OAAOQ,EAAI,GAAK,GAC/B,OAAKtD,EAAMkC,IAEHf,EAAME,EADJa,EAAI,EAAIb,EAAMmC,GAAWrC,KADZA,EAAME,GCrC/B,SAAgBuC,EAAMC,EAAQC,EAAOrB,GACnC,OAAOA,GAAO,QAASoB,EAAQC,GAAQrB,IAAQ,QAASoB,EAAQC,IAOlE,SAAgBC,EAAQlD,GACtB,OAAOf,MAAMkE,QAAQnD,IAAe,SAATA,EAAE,GAS/B,SAAgBwC,EAAOzD,EAAGqC,EAAGC,EAAGO,GAC9B,OAAOA,GAAO,OAAQwB,EAAIrE,EAAGqC,EAAGC,GAAIO,IAAQ,OAAQwB,EAAIrE,EAAGqC,EAAGC,IAQhE,SAAgBwB,EAAO7C,GACrB,OAAOqD,EAAIC,MAAM,KAAMtD,EAAE,IAQ3B,SAAgBuD,EAAMvD,GACpB,OAAQkD,EAAQlD,GAAYA,EAAE,GAAK,MAAQ,OAAtB,KAOvB,SAAgBwD,EAAYxD,GAC1B,MAAoB,SAAbuD,EAAMvD,GAOf,SAAgByD,EAAWzD,GACzB,MAAoB,QAAbuD,EAAMvD,GAOf,SAAgB0D,EAAK1D,GACnB,OAAOkD,EAAQlD,IAAsB,IAAhBA,EAAE,GAAGS,OAQ5B,SAAgBmB,EAAI5B,GAClB,OAAiB,IAAVA,EAAE,IAAa,EAAI,EAQ5B,SAAgBgD,EAAOhD,GACrB,OAAiB,IAAVA,EAAE,IAAaA,EAAE,GAAG,GAAKA,EAAE,GAAG,GAOvC,SAAgBiD,EAAMjD,GACpB,OAAiB,IAAVA,EAAE,IAAaA,EAAE,GAAG,GAAKA,EAAE,GAAG,GAOvC,SAAgB2D,EAAO3D,GACrB,OAAmB,EAAZgD,EAAOhD,GAAoB,GAAXiD,EAAMjD,GAW/B,SAAgB4D,EAAI5D,GAClB,IAAIyC,EAAIO,EAAOhD,GACf,OAAO,EAAIyC,EAAI,GAAKjD,KAAKyC,MAAU,EAAJQ,EAAQ,IAIzC,SAASoB,EAAQC,GACf,IAAIC,KACJ,OAAO,SAASpE,GACd,MAAmB,iBAARA,EAAyB,KAC7BoE,EAAMpE,KAASoE,EAAMpE,GAAOmE,EAAGnE,KAgC1C,SAAgBqE,EAAWjF,GACzB,OAAOkF,GAAUlF,IAAMmF,GAASnF,GASlC,SAAgBoF,EAAYnE,GAC1B,OAAOwD,EAAYxD,GAAKA,EAAIiE,GAAUjE,GAQxC,SAAgBoE,EAAWpE,GACzB,OAAOyD,EAAWzD,GAAKA,EAAIkE,GAASlE,GAQtC,SAAgBqE,EAAQrE,GACtB,OAAOkD,EAAQlD,GAAKA,EAAIgE,EAAWhE,GAQrC,SAAgBsE,EAAQtE,GACtB,OAAKwD,EAAYxD,GACVuE,EAAQjB,MAAM,KAAMT,EAAO7C,IADN,KAS9B,SAAgBwE,EAAOxE,GACrB,IAAKyD,EAAWzD,GAAI,OAAO,KAE3B,IAAIyE,EAAI5B,EAAO7C,GAEXhB,EAAMyF,EAAE,GAAK,EAAI,EAAIA,EAAE,GAC3B,OAAOzE,EAAE,GAAKhB,EAAMuD,EAAOvD,EAAKyF,EAAE,IAQpC,SAAgBC,EAAS1E,GACvB,OAAOsE,EAAQtE,IAAMwE,EAAOxE,GAM9B,SAAS2E,EAAUC,EAAIlF,EAAOC,GAC5B,OAAO,SAASmE,GACd,OAAO,SAASe,GAGd,GAFQD,EAAGC,GAEJ,OAAOf,EAAGe,GAEjB,IAAI7E,EAAIN,EAAMmF,GAEd,OAAO7E,EAAIL,EAAImE,EAAG9D,IAAM,OCzO9B,SAYS8E,EAAKpD,EAAG1B,GACf,IAAIoC,EAAImB,EAAMvD,GACd,IAAKoC,EAAG,OAAO,KACf,IAAIK,EAAIO,EAAOtB,GAAKsB,EAAOhD,GAC3B,GAAI0D,EAAK1D,GAAI,OAAQ,QAASyC,IAC9B,IAAIpB,EAAI4B,EAAMvB,GAAKuB,EAAMjD,GACzB,GAAU,SAANoC,EAAc,OAAQ,QAASK,EAAGpB,IACtC,IAAIoD,EAAId,EAAOjC,GAAKiC,EAAO3D,GAAK,GAAK,EAAI,EACzC,OAAQ,QAASyE,EAAIhC,EAAGgC,EAAIpD,GAAIoD,GAuBlC,SAAgBM,EAAU3D,EAAG4D,GAC3B,GAAyB,IAArBC,UAAUxE,OACZ,OAAO,SAASuE,GACd,OAAOD,EAAU3D,EAAG4D,IAExB,IAAIE,EAAKb,EAAQjD,GACb+D,EAAKd,EAAQW,GACbI,EAAI3B,EAAWyB,GAAMJ,EAAKI,EAAIC,GAAM1B,EAAW0B,GAAML,EAAKK,EAAID,GAAM,KACxE,OAAO9D,IAAM8D,GAAMF,IAAMG,EAAKC,EAAIV,EAASU,GAgB7C,SAAgBC,EAASjD,EAAGnB,GAC1B,OAAIgE,UAAUxE,OAAS,EAAU4E,EAASjD,GAAGnB,GACtC,SAASA,GACd,OAAO8D,EAAU3C,EAAGW,EAAM9B,EAAG,EAAG,KCzDpC,SAASqE,EAAOlE,EAAG4D,GACjB,IAAK5D,IAAM4D,GAAK5D,EAAE,GAAGX,SAAWuE,EAAE,GAAGvE,OAAQ,OAAO,KACpD,IAAIgC,EAAIO,EAAOgC,GAAKhC,EAAO5B,GAC3B,GAAIsC,EAAKtC,GAAI,OAAO2B,EAAMN,GAAIjD,KAAKyC,MAAU,EAAJQ,EAAQ,IAAK,GACtD,IAAIpB,EAAI4B,EAAM+B,GAAK/B,EAAM7B,GACrBqD,EAAId,EAAOqB,GAAKrB,EAAOvC,GAAK,GAAK,EAAI,EACzC,OAAO2B,EAAM0B,EAAIhC,EAAGgC,EAAIpD,EAAGoD,GAsB7B,SAAgBc,EAASnE,EAAG4D,GAC1B,GAAyB,IAArBC,UAAUxE,OACZ,OAAO,SAASuE,GACd,OAAOO,EAASnE,EAAG4D,IAEvB,IAAIE,EAAKb,EAAQjD,GACb+D,EAAKd,EAAQW,GACbtD,EAAI4D,EAAOJ,EAAIC,GAEnB,OAAO/D,IAAM8D,GAAMF,IAAMG,EAAKzD,EAAI8C,EAAO9C,GAc3C,SAAgB8D,EAAUpE,EAAG4D,GAC3B,IAAItD,EAAI4D,EAAOjB,EAAQjD,GAAIiD,EAAQW,IACnC,OAAOtD,EAAIiC,EAAOjC,GAAK,KCjDzB,SAAS+D,EAAOC,GACd,OAAOA,GAAW,IAANA,EAgDd,SAAgBC,EAAI7B,EAAI8B,GACtB,OAAOX,UAAUxE,OAAS,EACtBkF,EAAI7B,GAAI8B,GACR,SAASC,GACP,OAAOC,GAAMD,GAAGF,IAAI7B,IAW5B,SAAgBiC,EAAQC,GACtB,OAAOF,GAAME,GAAKC,OAAOR,GAgB3B,SAAgBQ,EAAOnC,EAAI8B,GACzB,OAAOX,UAAUxE,OAAS,EACtBwF,EAAOnC,GAAI8B,GACX,SAASC,GACP,OAAOC,GAAMD,GAAGI,OAAOnC,IAO/B,SAASoC,EAAUlG,GACjB,IAAKA,EAAG,OAAQmG,EAAAA,EAChB,IAAI1D,EAAgB,EAAZO,EAAOhD,GAEf,OAAOyC,EAAQ,IADPQ,EAAMjD,KAAOR,KAAKyC,MAAMQ,EAAI,IAAM,KAK5C,SAAS2D,EAAQhF,EAAG4D,GAClB,OAAOkB,EAAU9E,GAAK8E,EAAUlB,GAGlC,SAASqB,EAASjF,EAAG4D,GACnB,OAAQoB,EAAQhF,EAAG4D,GAqBrB,SAAgBsB,EAAKV,EAAMW,GACzB,IAAIzC,EACmB,IAArBmB,UAAUxE,SAAyB,IAAT8F,EACtBH,GACS,IAATG,EAAiBF,EAA2B,mBAATE,EAAsBA,EAAOH,EAGtE,OADAR,EAAO3G,MAAMkE,QAAQyC,GAAQA,EAAKY,QAAUV,GAAMF,GAC3Ca,GAAO,SAAST,GACrB,OAAOA,EAAIM,KAAKxC,GAAImC,OAAOR,IAC1BG,GA4BL,SAASc,EAAMzF,GACb,OAAO0F,EAAG5D,EAAM,EAAG9B,EAAG,IAUxB,SAAgB2F,EAAOC,EAAOjB,GAC5B,IAAII,EAAMF,GAAMF,GACZkB,EAAMd,EAAIvF,OACVQ,GAAK4F,EAAQC,EAAMA,GAAOA,EAC9B,OAAOd,EAAIQ,MAAMvF,EAAG6F,GAAKC,OAAOf,EAAIQ,MAAM,EAAGvF,IA0C/C,SAAgB+F,GAAOC,EAAMrB,GAC3B,GAAyB,IAArBX,UAAUxE,OACZ,OAAO,SAASoF,GACd,OAAOmB,GAAOC,EAAMpB,IAGxB,IAAIG,EAAMF,GAAMF,GAChB,OAAOE,GAAMmB,GAAMtB,IAAI,SAAS1E,GAC9B,OAAO+E,EAAI/E,EAAI,IAAM,OAUzB,SAAgBiG,GAAatB,GAE3B,OAAoB,KADpBA,EAAOE,GAAMF,IACJnF,YACFyG,GAAatB,EAAKY,MAAM,IAAIW,OAAO,SAAShH,EAAKiH,GACtD,OAAOjH,EAAI4G,OACTnB,EAAKD,IAAI,SAASD,EAAGhF,GACnB,IAAI2G,EAAUD,EAAKZ,QAEnB,OADAa,EAAQC,OAAO5G,EAAK,EAAGkF,EAAK,IACrByB,UAOf,SAASE,GAAWvH,GAClB,OAAO0E,EAAS1E,IAAMA,EAExB,SAASwH,GAAU3C,GACjB,OAAO3B,EAAQ2B,GAAKH,EAASG,GAAK4C,GAAM5C,GAAKA,EAAEc,IAAI4B,IAAc1C,EAgBnE,SAAS4B,GAAO3C,EAAI8B,GAClB,GAAyB,IAArBX,UAAUxE,OACZ,OAAO,SAASoF,GACd,OAAOY,GAAO3C,EAAI+B,IAGtB,IAAIG,EAAMF,GAAMF,GAAMD,IAAItB,GAE1B,OAAOmD,GADG1D,EAAGkC,IC9Qf,SAAgB0B,GAAU9B,GACxB,IAAIxE,EAAI0E,GAAMF,GACd,OAAOxE,EAAEX,OAASsF,EAAQ3E,EAAEuE,IAAIJ,EAASnE,EAAE,MAAQA,EAgBrD,SAAgBuG,GAAYC,GAC1B,IAAIC,KACJD,EAAQ9B,GAAM8B,GACd,IAAK,IAAIlG,EAAI,EAAGA,EAAIkG,EAAMnH,OAAQiB,IAChCmG,EAAKC,KAAKvC,EAASqC,EAAMlG,EAAI,GAAIkG,EAAMlG,KAEzC,OAAOmG,EAyBT,SAAgBE,GAAUnC,EAAM7C,GAC9B,OAAIkC,UAAUxE,OAAS,EAAUsH,GAAUnC,GAAM7C,GAC1C,SAASiF,GACd,OAAOjC,EAAQJ,EAAIgB,EAAGqB,GAAS,MAAOpC,KCzF1C,SAAS9G,GAASC,EAAGC,GAAO,OAAOC,MAAMD,EAAM,GAAGE,KAAKH,GACvD,SAASI,GAAOC,GAAK,MAAoB,iBAANA,EACnC,SAESC,GAAYC,EAAMC,GACzB,OAAOC,KAAKC,IAAI,GAAIH,EAAO,IAAM,KAAOC,GAAU,KAoFpD,SAAgBG,GAAOC,EAAKC,EAASL,GACnC,GAAmB,iBAARI,EAAkB,OAAO,KACpC,IAAIE,EAAIC,GAAMC,KAAKJ,GACnB,IAAKE,IAAOD,GAAWC,EAAE,GAAK,OAAO,KAErC,IAAIG,GAAMC,OAAQJ,EAAE,GAAGK,cAAeC,IAAKN,EAAE,GAAGO,QAAQ,KAAM,OAC9DJ,EAAEK,GAAKL,EAAEC,OAASD,EAAEG,IACpBH,EAAEM,MAAQN,EAAEC,OAAOM,WAAW,GAAK,GAAK,EACxCP,EAAEQ,IAAmB,MAAbR,EAAEG,IAAI,IAAcH,EAAEG,IAAIM,OAAST,EAAEG,IAAIM,OACjD,IAAIC,EAAMC,GAAUX,EAAEM,MAAQN,EAAEQ,IAQhC,OAPAR,EAAEY,OAASF,EAAM,EAAI,GAAKA,EAAMA,EAAM,GAClCb,EAAE,KACJG,EAAEa,KAAOhB,EAAE,GACXG,EAAEV,KAAOoB,EAAM,IAAMV,EAAEa,IAAM,GAC7Bb,EAAEc,KAAOzB,GAAWW,EAAEV,KAAMC,IAE1BK,IAASI,EAAEe,QAAUlB,EAAE,IACpBG,EAIT,SAASgB,GAAQC,GAAK,OAAQ9B,GAAM8B,GAAUA,EAAI,EAAInC,GAAQ,KAAMmC,GAAKnC,GAAQ,IAAKmC,GAA7C,GACzC,SAASC,GAAQD,GAAK,OAAQ9B,GAAM8B,GAAU,GAAKA,EAAV,GAgBzC,SAAgBE,GAAOpC,EAAGqC,EAAGC,GAC3B,OAAU,OAANtC,QAA2B,IAANA,EAA0B,KAC/CA,EAAEuB,KAAaa,GAAMpC,EAAEuB,KAAMvB,EAAEyB,IAAKzB,EAAE8B,KACtC9B,EAAI,GAAKA,EAAI,EAAU,KACpBuC,GAAQC,OAAOxC,GAAKiC,GAAOI,GAAKF,GAAOG,GCpIhD,SAESlC,GAAOC,GAAK,MAAoB,iBAANA,EACnC,SAAS6I,GAAO7I,GAAK,MAAoB,iBAANA,EACnC,SAAS8I,GAAO9I,GAAK,YAAoB,IAANA,EACnC,SAASC,GAAYC,EAAMC,GACzB,OAAOC,KAAKC,IAAI,GAAIH,EAAO,IAAM,KAAOC,GAAU,KAoFpD,SAAgBG,GAAOC,EAAKC,EAASL,GACnC,GAAmB,iBAARI,EAAkB,OAAO,KACpC,IAAIE,EAAIC,GAAMC,KAAKJ,GACnB,IAAKE,IAAOD,GAAWC,EAAE,GAAK,OAAO,KAErC,IAAIG,GAAMC,OAAQJ,EAAE,GAAGK,cAAeC,IAAKN,EAAE,GAAGO,QAAQ,KAAM,OAC9DJ,EAAEK,GAAKL,EAAEC,OAASD,EAAEG,IACpBH,EAAEM,MAAQN,EAAEC,OAAOM,WAAW,GAAK,GAAK,EACxCP,EAAEQ,IAAmB,MAAbR,EAAEG,IAAI,IAAcH,EAAEG,IAAIM,OAAST,EAAEG,IAAIM,OACjD,IAAIC,EAAMC,GAAUX,EAAEM,MAAQN,EAAEQ,IAQhC,OAPAR,EAAEY,OAASF,EAAM,EAAI,GAAKA,EAAMA,EAAM,GAClCb,EAAE,KACJG,EAAEa,KAAOhB,EAAE,GACXG,EAAEV,KAAOoB,EAAM,IAAMV,EAAEa,IAAM,GAC7Bb,EAAEc,KAAOzB,GAAWW,EAAEV,KAAMC,IAE1BK,IAASI,EAAEe,QAAUlB,EAAE,IACpBG,EA6CT,SAAgBV,GAAM6I,GACpB,IAAKhJ,GAAMgJ,IAASF,GAAME,KAAUA,GAAQ,GAAKA,EAAO,IAAK,OAAQA,EACrE,IAAInI,EAAIN,GAAMyI,GACd,OAAOnI,GAAKkI,GAAMlI,EAAEV,MAAQU,EAAEV,KAAO,KC/HvC,SAAgB8I,GAAOC,GACrB,OAAIpJ,MAAMkE,QAAQkF,IAAuB,IAAfA,EAAI5H,OACZ,EAAT4H,EAAI,GAAkB,GAATA,EAAI,GAAU,GAC7B/I,GAAK+I,GAqBd,SAAgBF,GAAKnJ,EAAKsJ,GACxB,OAAY,IAARtJ,IAAwB,IAARA,EACX,SAASa,GACd,OAAOsI,GAAKtI,EAAGb,KAEnBA,EAAMQ,KAAK+I,MAAMvJ,KACI,IAAXsJ,EAAkBE,GAASC,IACxBzJ,EAAM,KACXQ,KAAKyC,MAAMjD,EAAM,IAAM,IClCjC,SAASuJ,GAAM1I,EAAGiE,GAEhB,OADAjE,KAAIA,GAAW,IAANA,IAAUL,KAAKC,IAAI,GAAII,GACzB,SAASgF,GAEd,OAAa,QADbA,EAAIf,EAAGe,IACa,KAAOhF,EAAIL,KAAK+I,MAAM1D,EAAIhF,GAAKA,EAAIgF,GAkB3D,SAAgB6D,GAAWC,EAAKC,EAAKT,GACnC,OAAIlD,UAAUxE,OAAS,EAAUiI,GAAWC,EAAKC,GAAKT,GAC/CI,GAAMK,EAAK,SAAS5I,GACzB,IAAIH,EAAIgJ,GAAW7I,GACnB,OAAOH,EAAIL,KAAKC,IAAI,GAAII,EAAI,IAAM,IAAM8I,EAAM,OA0BlD,SAAgBG,GAAiBH,EAAKC,EAAK9H,GACzC,OAAImE,UAAUxE,OAAS,EAAUqI,GAAiBH,EAAKC,GAAK9H,GACrDyH,GAAMK,EAAK,SAAS9H,GACzB,OAAO,IAAMtB,KAAKuJ,IAAIjI,GAAQtB,KAAKuJ,IAAIJ,IAAQnJ,KAAKuJ,IAAI,GAAK,KC5CjE,SAAS9E,GAAU+E,GACjB,MAAoB,iBAATA,EAA0B,KAC9BjF,GAAMiF,KAAUjF,GAAMiF,GAAQtJ,GAAMsJ,IA2E7C,SAAgBA,GAAK/H,GACnB,IAAIjB,EAAImE,EAAYlD,GACpB,OAAOjB,EAAIsE,EAAQtE,GAAK,KA2B1B,SAAgBiJ,GAAMhI,GACpBiI,QAAQC,KACN,0EAEF,IAAInJ,EAAImE,EAAYlD,GACpB,IAAKjB,EAAG,OAAO,KACf,IAAIyE,EAAI5B,EAAO7C,GACf,OAASM,KAAMmE,EAAE,GAAIjE,IAAKiE,EAAE,GAAI5D,IAAK4D,EAAE,IAwBzC,SAAS2E,GAAQJ,GACf,OAAO,SAAS/H,GACd,IAAIjB,EAAIiJ,GAAMhI,GACd,OAAOjB,EAAIA,EAAEgJ,GAAQ,MAyCzB,SAAgBK,GAASlB,GACvB,IAAInI,EAAImE,EAAYgE,GACpB,OAAOnI,EAAIgD,EAAOhD,GAAK,KA2BzB,SAAgBK,GAAGY,GACjB,IAAIjB,EAAImE,EAAYlD,GACpB,OAAOjB,EAAIsE,GAAStE,EAAE,IAAKgD,EAAOhD,MAAQ,KAoB5C,SAAgBsJ,GAAYvG,GAC1B,IAAI6E,KAEJ,OADAA,EAAME,KAAKnB,EAAG4C,GAAMxG,IACH,OAAb6E,EAAM,GAAoB,MAC9BA,EAAME,KAAK/E,GACX6E,EAAME,KAAKnB,EAAG6C,GAAKzG,IACZ6E,GC5NT,SAAgB9F,GAAM9C,GACpB,OAAO+C,IAAO/C,EAAM,GAAK,GAG3B,SAASyK,GAAQ7H,GAAO,OAAgB,IAATA,EAAa,IAAM,GAClD,SAAS5C,GAAK6C,EAAQhB,GAAO,OAAOgB,EAAS,EAAIhB,EAyCjD,SAAgBM,GAAOU,EAAQrB,EAAKK,EAAKe,GACvC,OAAO6H,GAAO7H,GAAO5C,GAAI6C,EAAQhB,GAAO0B,GAAOV,EAAQrB,GA0BzD,SAAS1B,GAASC,EAAGkC,GAAK,OAAOhC,MAAMO,KAAK8C,IAAIrB,GAAK,GAAG/B,KAAKH,GAY7D,SAAgBwD,GAAQvD,EAAKwB,GAC3B,IAAI4B,EAAmB,iBAARpD,EAAmB8C,GAAKtC,KAAK8C,IAAItD,IAAQA,EACxD,OAAY,IAARwB,EAAwB,MAAN4B,EAAY,IAAM,KACtB,IAAT5B,GAAoB,MAAN4B,EAAkB,IAChC5B,EAAM,EAAU1B,GAAQ,IAAK0B,GAC7BA,EAAM,EAAU1B,GAAQ,IAAW,MAANsD,EAAY5B,EAAMA,EAAM,GAClD,KCrCd,SAAgByI,GAAMS,GACpB,IAAIhI,EAAI0C,EAAWsF,GACnB,IAAKhI,EAAG,OAAO,KACf,IAAI+C,EAAI5B,EAAOnB,GACf,OAAS1C,IAAKyF,EAAE,GAAK,EAAW,EAAPA,EAAE,GAAQjE,IAAKiE,EAAE,GAAI7C,IAAKF,EAAE,IAavD,SAAgBiI,GAAUV,GACxB,IAAKA,GAASA,EAAMjK,IAAM,EAAG,OAAO,KACpC,IAAI4K,EAAOpK,KAAKyC,MAAMgH,EAAMjK,IAAM,GAElC,OAAOmC,GADM8H,EAAMjK,IAAM,EAAI4K,EACRX,EAAMzI,KAAO,EAAGoJ,EAAMX,EAAMrH,KA6DnD,SAAgBiI,GAAGH,GACjB,IAAIhI,EAAI0C,EAAWsF,GACf3K,EAAI2C,EAAIkC,EAAIlC,GAAKlC,KAAK+I,MAAMmB,GAChC,OAAOI,MAAM/K,GAAK,KAAOgL,GAAQvK,KAAK8C,IAAIvD,GAAK,IChLjD,SAKSiL,GAASC,GAChB,OAAOC,SAAStJ,GAAOqJ,GAAM,GAE/B,SAASE,GAASnK,GAEhB,OADAA,EAAIqE,EAAQrE,IACD4D,EAAI5D,GAAK,KAetB,SAAgBY,GAAOqJ,GACrB,GAAIG,GAASH,GAAM,OAAOA,EAC1B,IAAIjF,GAAK,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,GAI1C,OAHAW,EAAIwE,GAAUF,GAAKI,QAAQ,SAAS3I,GAClCsD,EAAEtD,GAAK,IAEFsD,EAAE9F,KAAK,IAoChB,SAAgBoL,GAAML,EAAKM,GACzBA,GAA0B,IAAdA,EACZ,IAAIC,EAAS5J,GAAOqJ,GAAKQ,MAAM,IAC/B,OAAO1E,EACLyE,EAAO7E,IAAI,SAAS+E,EAAGhJ,GACrB,IAAI0D,EAAIwB,EAAOlF,EAAG8I,GAClB,OAAOD,GAAsB,MAATnF,EAAE,GAAa,KAAOA,EAAElG,KAAK,OAuBvD,SAAgBkL,GAASH,GACvB,OAAOnK,GAAMuC,KAAK4H,GAWpB,SAAgBU,GAAUV,GACxB,OAAOlE,EACLnF,GAAOqJ,GACJQ,MAAM,IACN9E,IAAI,SAASlB,EAAG/C,GACf,MAAa,MAAN+C,EAAYmG,GAAKlJ,GAAK,QAgBrC,SAAgBmJ,GAAWL,EAAQxC,GAIjC,OAHAkB,QAAQC,KACN,kEAEuB,IAArBlE,UAAUxE,OACL,SAAS2B,GACd,OAAOyI,GAAWL,EAAQpI,KAEzB4F,IAAOA,EAAQ,MACb2C,GAAUH,GAAQ7E,IAAIZ,EAAUiD,KAYzC,SAAgB8C,GAAQC,EAAIC,GAC1B,OAAyB,IAArB/F,UAAUxE,OACL,SAAS1B,GACd,OAAO+L,GAAQC,EAAIhM,IAEhB6B,GAAOmK,KAAQnK,GAAOoK,GAgB/B,SAAgBC,GAAShB,EAAK5H,GAC5B,OAAyB,IAArB4C,UAAUxE,OACL,SAAS2B,GACd,OAAO6I,GAAShB,EAAK7H,MAEzBC,EAAO2H,GAAS3H,IACD2H,GAASC,MAAU5H,EAgBpC,SAAgB6I,GAAWjB,EAAK5H,GAC9B,OAAyB,IAArB4C,UAAUxE,OACL,SAAS2B,GACd,OAAO8I,GAAWjB,EAAK7H,MAE3BC,EAAO2H,GAAS3H,IACD2H,GAASC,MAAU5H,EAgBpC,SAAgB8I,GAASlB,EAAK9B,GAC5B,OAAIlD,UAAUxE,OAAS,EAAU0K,GAASlB,GAAK9B,IAC/C8B,EAAMrJ,GAAOqJ,GACN,SAAS9B,GACd,MAA+B,MAAxB8B,EAAIE,GAAShC,MAexB,SAAgBlC,GAAOgE,EAAKrC,GAC1B,OAAyB,IAArB3C,UAAUxE,OACL,SAASQ,GACd,OAAOgF,GAAOgE,EAAKhJ,IAEhB6E,GAAM8B,GAAO3B,OAAOkF,GAASlB,IC/OtC,SAKS9K,GAAM8B,GACb,MAAoB,iBAANA,EAGhB,SAASmK,GAAMnK,GACb,OAAO9B,GAAM8B,GAAKA,EAAImH,GAAOnH,GAG/B,SAASoK,GAAKrG,EAAG/D,GACf,IAAK,IAAIG,KAAQH,IAAKG,EAAEH,GAAKA,EAAI+D,GACjC,OAAO5D,EAGT,SAASkK,GAAMtG,EAAG/D,GAChB,IAAK,IAAIG,KAAQH,IAAKG,EAAEH,GAAK+D,EAAI/D,GACjC,OAAOG,EAGT,SAASmK,GAAInK,EAAG4D,GACd,OAAa,OAAN5D,GAAoB,OAAN4D,KAEjB5D,EAAI4D,EAAIqG,GAAKjK,EAAG4D,EAAI5D,EAAI,GAAKkK,GAAMlK,EAAGA,EAAI4D,EAAI,GAmBpD,SAAgBwG,GAAQ5F,GACtB,OAAOE,GAAMF,GACVD,IAAIyF,IACJjE,OAAO,SAAS/B,EAAGnE,EAAGS,GACrB,GAAU,IAANA,EAAS,OAAO6J,GAAInG,EAAGnE,GAC3B,IAAIwK,EAAOrG,EAAEA,EAAE3E,OAAS,GACxB,OAAO2E,EAAE2B,OAAOwE,GAAIE,EAAMxK,GAAGuF,MAAM,MAezC,SAAgBkF,GAAU9F,EAAM0C,GAC9B,OAAO3C,EAAIwC,IAAgB,IAAXG,GAAkBkD,GAAQ5F,IA6B5C,SAAgB+F,GAAS1B,EAAK2B,GAC5B,OAAyB,IAArB3G,UAAUxE,OACL,SAASoF,GACd,OAAO8F,GAAS1B,EAAKpE,IAGlBI,GAAOgE,EAAKyB,GAAUE,ICxF/B,SAAgBC,GAAOpH,GACrB,QAASA,EAAI,GAAKA,EAAI,GAoBxB,SAAgBqH,GAAS/M,GACvB,MAAO,OAAOsD,KAAKtD,GAOrB,SAAgBgN,GAAUhN,GACxB,MAAO,OAAOsD,KAAKtD,GAkBrB,SAASD,GAAQC,EAAGC,GAClB,OAAOC,MAAMD,EAAM,GAAGE,KAAKH,GAY7B,SAAgBiN,GAAM/K,GACpB,OAAQA,EAASA,EAAI,EAAInC,GAAQ,KAAMmC,GAAKnC,GAAQ,IAAKmC,GAA7C,GCtDd,SAASgL,GAAM7J,EAAGvC,GAChB,OAAQuC,EAAQA,EAAI,IAAMvC,EAAdA,EAGd,SAASqM,GAASjL,GAChB,OAAOgL,GAAM5G,EAAS,IAAKpE,GAAI,SAGjC,SAASkL,GAAQC,GACf,OAAO1J,GAAO2J,GAAMC,QAAQF,IAG9B,SAASG,GAAU1M,GAEjB,OADAA,EAAIA,EAAE2M,OAAOC,eACgB,IAAtBJ,GAAMC,QAAQzM,GAAY,KAAOA,EAe1C,SAAgBoJ,GAAMtJ,GACpB,GAAmB,iBAARA,EAAkB,OAAO,KACpC,IACI+M,EADAC,EAAMhN,EAAI2M,QAAQ,KAEtB,IAAa,IAATK,EAAY,CACd,IAAI3M,EAAIK,GAAGV,GACX+M,EAAM1M,GACAgI,MAAOhI,EAAGoM,KAAM,UAChBpE,OAAO,EAAOoE,KAAMG,GAAU5M,SAEpC+M,GAAQ1E,MAAO3H,GAAGV,EAAI6G,MAAM,EAAGmG,IAAOP,KAAMG,GAAU5M,EAAI6G,MAAMmG,EAAM,KAExE,OAAOD,EAAIN,KAAOM,EAAM,KA4D1B,SAAgBE,GAASC,EAAKH,GAC5B,OAAyB,IAArBzH,UAAUxE,OACL,SAASqM,GACd,OAAOF,GAASC,EAAKC,MAEzBD,EAAM5D,GAAM4D,KACAA,EAAI7E,MAAc,MAC9B0E,EAAMzD,GAAMyD,KACCA,EAAI1E,MAEViE,GADK5G,EAASqH,EAAI1E,MAAOmE,GAAQU,EAAIT,MAAQD,GAAQO,EAAIN,OAC5CS,EAAIT,MAFO,KA8FjC,SAAgBW,GAAWL,GACzB,IAAII,EAAI7D,GAAMyD,GACd,IAAKI,IAAMA,EAAE9E,MAAO,OAAO,KAC3B,IAAIgF,EAAUb,GAAQW,EAAEV,MAExB,OADU/C,GAASyD,EAAE9E,OACRgF,EASf,SAAgBC,GAAUP,GACxB,OAAOV,GAAMe,GAAWL,IClP1B,SAASQ,GAAG9N,GACV,OAAOA,EA2BT,SAAgB+N,GAAWC,EAAK1N,GAC9BA,EAAQA,GAASwN,GACjB,IAAIG,KACAC,EAAQC,OAAOC,KAAKJ,GACpBK,KAWJ,OAVAH,EAAMjD,QAAQ,SAASyC,GACrB,IAAIY,EAAQhO,EAAM0N,EAAIN,GAAG,IACzBO,EAAMP,GAAKY,EACPN,EAAIN,GAAG,IACTM,EAAIN,GAAG,GAAGzC,QAAQ,SAASsD,GACzBN,EAAMM,GAASD,EACfD,EAAQ3F,KAAK6F,QAajBC,IAAK,SAAS3M,GACZ,OAAOoM,EAAMpM,IAYfuM,KAAM,SAASK,EAAK5H,GAClB,IAAIuH,EAAOK,EAAMP,EAAMvG,OAAO0G,GAAWH,EAAM9G,QAC/C,MAAyB,mBAAXP,EACVuH,EACAA,EAAKvH,OAAO,SAAS6G,GACnB,OAAO7G,EAAO6G,EAAGO,EAAMP,QAoBnC,SAAgBgB,GAASC,EAAM5M,GAC7B,IAAI6M,EAAyB,iBAAV7M,EACf8M,EAAwB,mBAAV9M,EACd+M,EAAeH,EAAKP,MAAK,GAAOrG,OAAO,SAASxB,EAAK+G,GAEvD,OADA/G,EAAI/E,GAAOmN,EAAKH,IAAIlB,KAASA,EACtB/G,OAGT,OAAO,SAASiC,GAGd,OAAO7B,EADIuE,GADX1C,EAAQtB,EAAKX,EAAItF,GAAIuH,KAGdjC,IAAI,SAASsE,EAAKvI,GACrB,IAAII,EAAOoM,EAAajE,GACxB,IAAKnI,EAAM,OAAO,KAClB,IAAIkG,EAAQJ,EAAMlG,GAClB,OAAOsM,EACHhG,EAAQ7G,EAAQW,EAChBmM,EAAO9M,EAAMW,EAAMkG,IAAUlG,EAAMkG,OCpF/C,SAAgB4F,GAAI9L,EAAMkG,GACxB,GAAyB,IAArB/C,UAAUxE,OACZ,OAAO,SAAS2B,GACd,OAAOwL,GAAI9L,EAAMM,IAErB,IAAI+L,EAAOJ,GAAKH,IAAI9L,GACpB,OAAOqM,EAAOpG,GAAUoG,EAAMnG,GAAS,KA2DzC,SAAgB2C,GAAU3B,GAExB,OAAO4E,GADKlO,GAAMsJ,GACDlH,MAAM,OA8BzB,SAAgBpC,GAAMC,GACpB,GAAmB,iBAARA,EAAkB,OAAO,KACpC,IAAI+B,EAAI/B,EAAI2M,QAAQ,KAChBtE,EAAQG,GAAKxI,EAAIyO,UAAU,EAAG1M,MAAO,EAEzC,OAASsG,MAAOA,EAAOlG,KADZkG,EAAQrI,EAAIyO,UAAU1M,EAAI,GAAK/B,GCjG5C,SAAgB0O,KAAW,OAAOvO,GCKlC,SAAgB8N,GAAI9L,EAAMkG,GACxB,GAAyB,IAArB/C,UAAUxE,OACZ,OAAO,SAAS2B,GACd,OAAOwL,GAAI9L,EAAMM,IAErB,IAAI+L,EAAOJ,GAAKH,IAAI9L,GACpB,OAAOqM,EAAOpG,GAAUoG,EAAMnG,GAAS,KAezC,SAAgBJ,GAAM0G,GACpB,IAAItO,EAAIN,GAAM4O,GACVH,EAAOJ,GAAKH,IAAI5N,EAAE8B,MACtB,OAAOqM,EAAOpG,GAAUoG,EAAMnO,EAAEgI,OAASjC,EAAQJ,EAAIwC,GAAMmG,IAS7D,SAAgB3D,GAAU3B,GACxB,IAAIhJ,EAAIN,GAAMsJ,GACd,OAAO+E,GAAKH,IAAI5N,EAAE8B,UA6DpB,SAAgByM,GAAUvP,EAAKsP,GAC7B,GAAyB,IAArBrJ,UAAUxE,OACZ,OAAO,SAAS+N,GACd,OAAOD,GAAUvP,EAAKwP,IAE1B,IAAIC,EAASC,GAAWJ,GACxB,OAAOG,EAAS7H,EAAO5H,EAAKyP,MAG9B,SAASC,GAAWJ,GAElB,IAAK,IADDT,EAAM3G,GAAaU,GAAM0G,GAAO3I,IAAItF,KAC/BqB,EAAI,EAAGA,EAAImM,EAAIpN,OAAQiB,IAE9B,GAAIiN,GADOhH,GAAYkG,EAAInM,KACN,OAAOmM,EAAInM,GAElC,OAAO,KAGT,SAASiN,GAAU/I,GACjB,IAAK,IAAIlE,EAAI,EAAGA,EAAIkE,EAAKnF,OAAQiB,IAC/B,GAAmB,MAAfkE,EAAKlE,GAAG,GAAY,OAAO,EAEjC,OAAO,EAmBT,SAAgBhC,GAAMsJ,GACpB,IAAIhJ,EAAIqO,KAAQtO,KAAKiJ,GACrB,OAAKhJ,EAGGA,EAAE,GAGG,MAATA,EAAE,IAAuB,MAATA,EAAE,IACd8B,KAAM9B,EAAE,GAAKA,EAAE,GAAIgI,MAAOhI,EAAE,GAAKA,EAAE,KACnC8B,KAAM9B,EAAE,GAAIgI,MAAOhI,EAAE,GAAKA,EAAE,GAAKA,EAAE,KAJrC8B,KAAM9B,EAAE,GAAIgI,MAAOhI,EAAE,GAAKA,EAAE,KAJjB8B,KAAMkH,EAAMhB,OAAO,GC7ItC,SAAgB4G,GAAW5P,EAAKwB,EAAKqO,GACnC,OAAO7C,GAAMxL,GAAOsO,GAAK9P,EAAM,IAAM6P,GAAW,IA6DlD,SAAgBE,GAAgBpP,GAC9B,IAAIE,EAAImP,GAAMjP,KAAKJ,GACnB,IAAKE,EAAG,OAAO,KACf,IAAIb,EAAMiQ,GAAIpP,EAAE,GAAG4M,eAAiB,EAChCjM,EAAMX,EAAE,GAAGY,OAEf,MADgB,MAAZZ,EAAE,GAAG,KAAYW,GAAOA,IACnB0O,KAAMvF,IAAY3K,IAAKA,EAAKwB,IAAKA,EAAKoB,IAAK,IAAME,KAAMjC,EAAE,IvB1GpE,IAAIC,GAAQ,oDAqCRa,IAAa,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,IAgE/BW,GAAU,UC3GVG,GAAY,IAAI0N,OAAO,+EA4CvBhN,IAAS,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,IAE3BJ,GAAQ,UCrCRW,IAAU,EAAG,EAAG,GAAI,EAAG,EAAG,EAAG,GAM7BC,GAAaD,GAAOiD,IAJxB,SAAelD,GACb,OAAOjD,KAAKyC,MAAU,EAAJQ,EAAQ,MA8BxBK,IAAS,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,GCoGpBmB,GAAYJ,EAAQ,SAAS9E,GACtC,IAAIiB,EAAIoP,EAAUrQ,GAClB,OAAOiB,EAAIwC,EAAOxC,EAAEM,KAAMN,EAAEQ,IAAKR,EAAEa,KAAO,OASjCqD,GAAWL,EAAQ,SAAS9E,GACrC,IAAIiB,EAAIqP,EAAStQ,GACjB,OAAKiB,GACEA,EAAIwC,EAAOxC,EAAE6B,OAAS,EAAG7B,EAAEQ,IAAKR,EAAEa,IAAKb,EAAE4B,KADjC,OAmGN0N,GAAS3K,EAAUnB,EAAaS,GAAWK,GAS3CiL,GAAQ5K,EAAUlB,EAAYS,GAAUM,GASxCgL,GAAU7K,EAAUzB,EAASc,EAAYU,wWGxPhD+C,GAAQxI,MAAMkE,QA0BP2C,GAzCX,SAIe2J,GACb,OAAO,SAASpO,GACd,YAAaqO,IAANrO,KAEHpC,MAAMkE,QAAQ9B,GACZA,EACa,iBAANA,EAAiBA,EAAEmL,OAAO/B,MAAMgF,IAAQpO,KA+BhC,wBA8HdsO,GAAUlJ,GAAO,SAAST,GAGnC,IAFA,IAAItE,EAAGU,EACHvC,EAAImG,EAAIvF,OACLZ,GACL6B,EAAKlC,KAAKoQ,SAAW/P,IAAO,EAC5BuC,EAAI4D,EAAInG,GACRmG,EAAInG,GAAKmG,EAAItE,GACbsE,EAAItE,GAAKU,EAEX,OAAO4D,6FA+BT,SAA0Ba,EAAOjB,GAC/B,OAAOa,GAAO,SAAST,GACrB,IAAIc,EAAMd,EAAIvF,OACVQ,GAAK4F,EAAQC,EAAMA,GAAOA,EAC1B+I,EAAO7J,EAAIQ,MAAMvF,EAAG6F,GACpBgJ,EAAO9J,EAAIQ,MAAM,EAAGvF,GAEpBlC,EAAIyG,EAAUqK,EAAK/I,EAAM7F,EAAI,GAAI6O,EAAK,IAC1C,GAAI/Q,EAAI,EAAG,CACT,IAAI6K,EAAOpK,KAAKyC,MAAMlD,EAAI,IACtB8H,EAAQ,EAAGgJ,EAAOA,EAAKlK,IAAIe,EAAMkD,IAChCkG,EAAOA,EAAKnK,IAAIe,GAAOkD,IAE9B,OAAOiG,EAAK9I,OAAO+I,IAClBlK,6FEhOD9F,GAAQ,oDAqCRa,IAAa,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,IAgE/BW,GAAU,UCrGVxB,GAAQ,oDAqCRa,IAAa,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,ICZ/B8H,GAAQ,+BAA+BgC,MAAM,KAC7CjC,GAAS,+BAA+BiC,MAAM,2CCiCvCsF,GAASrH,GAAW,IAAK,GA4BzBN,GAASU,GAAiB,IAAK,gFAY1C,SAAqBhI,EAAMkP,GACzB,OAAOC,GAAW7H,GAAOtH,GAAOkP,UAalC,SAAsBE,EAAMpP,GAC1B,IAAIkE,EAAI+K,GAAOG,IAASA,EACpBzN,EAAIsN,GAAOjP,IAASA,EACxB,OAAOtB,KAAK+I,MAAc/I,KAAKuJ,IAAItG,EAAIuC,GAAKxF,KAAKuJ,IAAI,GAAnC,SCzFhBhF,MAiBOzE,GAAO8I,GAcP+H,GAAWF,GAaXnP,GAAOiP,GAiHPlP,GAAMuI,GAAQ,OAed9I,GAAO8I,GAAQ,QA2Bf5I,GAAM4I,GAAQ,OAiBrBI,GAAMtF,GAAS,MACfqF,GAAOrF,GAAS,4DAhKpB,SAAuBjD,GACrB,IAAIjB,EAAIiE,GAAUhD,GAClB,OAAOjB,EAAIA,EAAEY,OAAS,mBA6BxB,SAAqBK,GAEnB,OADAiI,QAAQC,KAAK,8CACNH,GAAK/H,uBA2Cd,SAA0BgI,GAExB,OADAC,QAAQC,KAAK,gEACNF,EAAQ9H,GAAM8H,EAAM3I,KAAM2I,EAAMzI,IAAKyI,EAAMpI,KAAO,sEAqH3D,SAAyBkC,GACvB,OAAOuG,GAAYvG,GAAOoE,OAAO,SAAStF,EAAQuO,GAChD,OAAKvO,EACEA,EAAOpB,OAAS2P,EAAK3P,OAAS2P,EAAOvO,EADxBuO,GAEnB,SCnPDrO,GAAQ,UCoGRsO,IAAM,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,GAEvCC,GAAK,0BAA0B7F,MAAM,KAqBrCV,IAAW,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,GAyB5ChI,GAAQ,UA6BDwO,GAAShB,GAAM,SAAS7N,GACjC,IAAI+C,EAAI5B,EAAOnB,GAIf,OAAOc,GAFK,EAAIiC,EAAE,IAAM,EACE,MAAhB1C,GAAM0C,EAAE,KAAeA,EAAE,KAAOA,EAAE,GAAK,GACxBA,EAAE,GAAI7C,EAAIF,MAiB1B8O,GAAWjB,GAAM,SAAS7N,GAEnC,IAAI2B,EAAMR,EAAOnB,GAIjB,OAFe,IAAX2B,EAAI,IAAuB,IAAXA,EAAI,KAAUA,EAAI,GAAK,GAEpCb,EAAOa,EAAI,GAAIA,EAAI,GAAIA,EAAI,GAAIzB,EAAIF,mCArM5C,SAA2BgI,GACzB,IAAIhI,EAAI0C,EAAWsF,GACnB,OAAOhI,EAAI8C,EAAO9C,GAAK,UAazB,SAAoBgI,GAClB,IAAI1J,EAAIiJ,GAAMS,GACd,OAAO1J,EAAIA,EAAEhB,IAAM,YAerB,SAAsB0K,GACpB,IAAI1J,EAAIiJ,GAAMS,GACd,OAAO1J,EAAIA,EAAEhB,IAAMgB,EAAE4B,IAAM,sCAoD7B,SAA0B8H,GACxB,IAAIhI,EAAI0C,EAAWsF,GACnB,OAAOhI,EAAIiC,EAAOjC,GAAK,oBAmBzB,SAA8B1C,GAC5B,IAAIyF,EAAIzF,EAAM,GAAK,EAAI,EACnBiC,EAAIzB,KAAK8C,IAAItD,GACbwP,EAAIvN,EAAI,GACRI,EAAI7B,KAAKyC,MAAMhB,EAAI,IACvB,OAAOwD,GAAK4L,GAAG7B,GAAK,EAAInN,GAAKiP,GAAG9B,eAuClC,SAAqB9E,GACnB,IAAIhI,EAAI0C,EAAWsF,GACnB,OAAOhI,EAAIK,GAAMc,EAAOnB,GAAG,IAAM,8BC1G/B5B,GAAQ,aAcR8K,GAAO,sCAAsCH,MAAM,uCA1DvD,SAAsB7C,GAEpBsB,QAAQC,KAAK,gDACb,IAAIsH,EAAM9K,EAAItF,GAAIuH,GAClB,IAAK6I,EAAIhQ,OAAQ,OAAOgQ,EACxB,IAAIzI,EAAQyI,EAAI,GAGhB,OAAO5F,GADOjE,EAAOuD,GAASnC,GAAQpH,GAAO6P,GAAKhG,MAAM,KAAKvL,KAAK,IACvC8I,yBA+B7B,SAA4BiC,EAAKyG,GAE/B,OADAxH,QAAQC,KAAK,wDACNmB,GAAML,EAAKyG,4DA0EpB,SAAsBtP,EAAG4D,GAEvB,OADAkE,QAAQC,KAAK,gDACN2B,GAAQ1J,EAAG4D,uBAoBpB,SAAuB5D,EAAG4D,GAExB,OADAkE,QAAQC,KAAK,kDACN8B,GAAS7J,EAAG4D,2BAoBrB,SAAyB5D,EAAG4D,GAE1B,OADAkE,QAAQC,KAAK,sDACN+B,GAAW9J,EAAG4D,6EC5HvB,SAAuBgD,EAAO4D,GAC5B,OAAOJ,GAAQI,GAAOjG,IAAIN,EAAS2C,4CC5ErC,SAAuBnC,GACrB,IAAI9G,EAAI,UAAUuN,QAAQzG,EAAE3F,eAC5B,OAAOnB,EAAI,EAAI,KAAOA,sBAiBxB,SAAyBA,GACvB,OAAO8M,GAAO9M,GAAK,UAAUwC,OAAOxC,GAAK,qCAgC3C,SAAsBA,GACpB,MAAa,KAANA,EACH,EACA+M,GAAS/M,IAAMA,EAAE0B,OAASsL,GAAUhN,GAAKA,EAAE0B,OAAS,iBCxDtD4L,IACF,SACA,SACA,WACA,SACA,aACA,UACA,UACA,QACA,SAGE3J,IAAU,EAAG,EAAG,GAAI,EAAG,EAAG,EAAG,EAAG,EAAG,GACnCiO,IAAU,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,GAAGhL,IAAI,SAAS1E,GACpD,OAAOyG,GAAUd,EAAO3F,GAAI,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,SAsOjD2P,GAAc3D,wCA3KzB,SAA0BjE,GACxB,OAAuB,OAAhBC,GAAMD,UAaf,SAAsB0D,GACpB,OAAQzD,GAAMyD,IAAQA,OAAW1E,OAAS,WAa5C,SAAqB0E,GACnB,OAAQzD,GAAMyD,IAAQA,OAAWN,MAAQ,+BAwC3C,SAA6BM,GAC3B,IAAIlM,EAAMuM,GAAWL,GACrB,OAAe,OAARlM,EACH,KACAA,EAAM,EACJgL,KAAU,EAAGhL,IAAMmF,IAAIN,EAAS,MAChCmG,IAAS,EAAGhL,IAAMmF,IAAIN,EAAS,aAevC,SAAsBsI,GACpB,OAAOA,EAAQtB,GAAM7F,QAAU6F,GAAM7F,MAAM,GAAI,cAYjD,SAA0BvF,GACxB,MAAoB,iBAANA,EAAiBiL,GAASjL,GAAK,cAa/C,SAAwBlC,GACtB,OAAOgN,GAAUhN,GACbmN,GAASnN,EAAE0B,QACXqL,GAAS/M,GAAKmN,IAAUnN,EAAE0B,QAAU,YAc1C,SAAsBiM,GACpB,IAAI1M,EAAIiJ,GAAMyD,GACd,OAAK1M,GAAMA,EAAEgI,MACND,GAAU4I,GAAOtE,GAAMC,QAAQtM,EAAEoM,OAAQpM,EAAEgI,OADvB,kDEpNzB+F,GAAOZ,8tHAAiB,SAASxN,GACnC,OAAOA,EAAI8K,MAAM,OAyCR6C,GAAQS,GAAKP,KAgGbqD,GAAS/C,GAASC,GAAM,6CA9EnC,SAAsB/E,GACpB,IAAI8H,EAAQpR,GAAMsJ,GAElB,OADY8H,EAAM9I,MAAQ4F,GAAIkD,EAAMhP,KAAMzB,GAAGyQ,EAAM9I,QAAU,OAG3DjC,EACEJ,EAAItF,GAAI2I,GAAMrD,IAAI,SAAS1E,EAAGS,EAAGsE,GAG/B,OAAOA,EAAIsG,QAAQrL,GAAKS,EAAI,KAAOT,+BAgC3C,SAA4B+H,GAC1B,OAAO2B,GAAU3B,GAAMvI,OAAS,wBC/G9BX,GAAQ,oDCSRiO,GAAOZ,+sHAAiB,SAASxN,GACnC,OAAOA,EAAI8K,MAAM,OAcR6C,GAAQS,GAAKP,KA8EbqD,GAAS/C,GAASC,GAAM,yEAfnC,SAA6B/E,GAC3B,OAAO2B,GAAU3B,GAAMvI,OAAS,sBA4BlC,SAAyB6N,GACvB,IAAImC,EAAM9K,EAAItF,GAAIiO,GACdG,EAASC,GAAW+B,GACxB,OAAOhC,EAASA,EAAOnC,QAAQmE,EAAI,IAAM,8BCrFvC3B,IAAQ,IAAK,KAAM,MAAO,KAAM,IAAK,KAAM,OA0B3CE,GAAQ,yEAuBRC,IAAQvN,EAAG,EAAGqP,GAAI,EAAGC,IAAK,EAAGC,GAAI,EAAGpM,EAAG,EAAGqM,GAAI,EAAGC,IAAK,8BAlE1D,SAAyBC,EAAQpJ,GAC/BA,EAAQ3H,GAAG2H,GAEX,IAAIqJ,EAAStL,GADbqL,EAASzL,EAAIjG,GAAO0R,IAEXzL,IAAI,SAASvG,GAClB,OAAOA,EAAE4I,SAIb,OAAIqJ,EAAO5Q,SAAW2Q,EAAO3Q,OAAe,KAErC4Q,EAAO1L,IAAI,SAASvD,EAAGV,GAC5B,IAAI1B,EAAIiJ,GAAM1D,EAASyC,EAAO5F,IAC9B,OAAOwM,GAAW5O,EAAEhB,IAAM,EAAGgB,EAAEQ,IAAK4Q,EAAO1P,GAAGI,gCAuBlD,SAAyBsP,EAAQpJ,GAC/B,OAAOrC,EAAI,SAASD,GAClB,IAAIN,EAAI2J,GAAgBrJ,GACxB,OAAON,EAAIL,EAAUK,EAAE8J,KAAMlH,GAAS5C,EAAEtD,KAAO,MAC9CsP,eAsBL,WACE,OAAOpC,mDCzDT,SAAwBpJ,GACtB,IAAIxE,EAAG4D,EAAGtD,EACNkG,EAAQ7B,EAAQJ,EAAIxB,EAAayB,IACjCkB,EAAMc,EAAMnH,OACZ6Q,GAAU,EAAG,EAAG,EAAG,EAAG,EAAG,GAC7B,IAAKlQ,EAAI,EAAGA,EAAI0F,EAAK1F,IACnB,IAAK4D,EAAI5D,EAAG4D,EAAI8B,EAAK9B,IAET,KADVtD,EAAImI,GAAGjG,EAAIgE,EAAM5C,IAAMpB,EAAIgE,EAAMxG,MACpBkQ,EAAO,GAAKA,EAAO,GAAK,EAC5B5P,EAAI,IAAG4P,EAAO,EAAI5P,GAAK4P,EAAO,EAAI5P,GAAK,GAGpD,OAAO4P,6BCxBT,SAAsB1J,GACpB,OAAOtB,EAAKsB,GAAO3B,OAAO,SAAShF,EAAGS,EAAGsE,GACvC,OAAa,IAANtE,GAAWT,IAAM+E,EAAItE,EAAI,QCkBhC6P,GAAShE,OAAOgE,OAChBC,GAAQD,MAAWE,GAAO1M,GAAW2M,GAAYC,WACrDH,GAAMzO,MAAQA,GACdyO,GAAMI,SAAWA,GACjBJ,GAAMrJ,KAAOA,GACbqJ,GAAM9H,IAAMnE,GACZiM,GAAMlS,KAAOA,GACbkS,GAAM1Q,KAAOA,GACb0Q,GAAM5F,MAAQA,GACd4F,GAAM9E,IAAMA,GACZ8E,GAAMK,YAAcA,GACpBL,GAAMM,SAAWA,GACjBN,GAAMO,SAAWA,GACjBP,GAAMQ,MAAQA,GAEdR,GAAMV,MAAQ,SAAS9H,GACrB,OAAOwI,GAAMV,MAAMlJ,MAAMoB,IAE3BuI,GAAOC,GAAMV,MAAOA,IACpBU,GAAMlD,MAAQ,SAAStF,GACrB,OAAOwI,GAAMlD,MAAM1G,MAAMoB,IAE3BuI,GAAOC,GAAMlD,MAAOA,IAEE,oBAAX2D,SAAwBA,OAAOC,MAAQV"} \ No newline at end of file diff --git a/package.json b/package.json index 8b55cb6..d75851d 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,12 @@ "main": "dist/tonal.min.js", "scripts": { "init": "lerna clean && lerna bootstrap && npm run build", - "prebuild": "lerna bootstrap", - "build": "lerna exec -- rollup -c ../../../rollup.config.js -o build/index.js -- index.js", + "format": "prettier --write packages/**/*.js", + "prebuild": "npm run format && lerna bootstrap", + "build": "lerna exec -- rollup -c ../../../rollup.config.js -- index.js", "dist": "rollup --config rollup.config.dist.js && ls -hall dist/*", - "prepublish": "npm run test && npm run dist && cp README.md ./packages/tonal/", + "prepublish": + "npm run test && npm run dist && cp README.md ./packages/tonal/", "pretest": "npm run build", "test": "jest --coverage --no-cache", "docs": "lerna run docs", @@ -21,26 +23,18 @@ "author": "danigb@gmail.com", "license": "MIT", "devDependencies": { - "eslint": "^3.14.1", - "eslint-config-standard": "^10.2.1", - "eslint-plugin-import": "^2.2.0", - "eslint-plugin-node": "^4.2.2", - "eslint-plugin-promise": "^3.5.0", - "eslint-plugin-standard": "^3.0.1", "jest": "^20.0.0", - "lerna": "2.0.0", - "postman-jsdoc-theme": "0.0.2", - "rollup": "^0.43.0", - "rollup-plugin-commonjs": "^8.0.2", + "lerna": "2.1.2", + "postman-jsdoc-theme": "0.0.3", + "prettier": "^1.6.1", + "rollup": "^0.49.2", + "rollup-plugin-commonjs": "^8.2.0", "rollup-plugin-json": "^2.1.1", "rollup-plugin-node-resolve": "^3.0.0", "rollup-plugin-uglify": "^2.0.0" }, "standard": { - "ignore": [ - "dist/*", - "build/*" - ] + "ignore": ["dist/*", "build/*"] }, "dependencies": {} } diff --git a/packages/core/array/index.js b/packages/core/array/index.js index 41aee48..aca4581 100644 --- a/packages/core/array/index.js +++ b/packages/core/array/index.js @@ -12,23 +12,24 @@ * * @module array */ -import { asPitch, isPitch, strPitch, pitch, fifths, focts } from 'tonal-pitch' -import { transpose as tr } from 'tonal-transpose' -import { semitones } from 'tonal-distance' +import { asPitch, isPitch, strPitch, pitch, fifths, focts } from "tonal-pitch"; +import { transpose as tr } from "tonal-transpose"; +import { semitones } from "tonal-distance"; -function split (sep) { - return function (o) { - return o === undefined ? [] - : Array.isArray(o) ? o - : typeof o === 'string' ? o.trim().split(sep) - : [o] - } +function split(sep) { + return function(o) { + return o === undefined + ? [] + : Array.isArray(o) + ? o + : typeof o === "string" ? o.trim().split(sep) : [o]; + }; } // utility -var isArr = Array.isArray -function hasVal (e) { - return e || e === 0 +var isArr = Array.isArray; +function hasVal(e) { + return e || e === 0; } /** @@ -52,7 +53,7 @@ function hasVal (e) { * asArr('A, B, c') // => ['A', 'B', 'c'] * asArr('1 | 2 | x') // => ['1', '2', 'x'] */ -export var asArr = split(/\s*\|\s*|\s*,\s*|\s+/) +export var asArr = split(/\s*\|\s*|\s*,\s*|\s+/); /** * Return a new array with the elements mapped by a function. @@ -76,12 +77,12 @@ export var asArr = split(/\s*\|\s*|\s*,\s*|\s+/) * var tonal = require('tonal') * tonal.map(tonal.transpose('M3'), 'C D E') // => ['E', 'F#', 'G#'] */ -export function map (fn, list) { +export function map(fn, list) { return arguments.length > 1 ? map(fn)(list) - : function (l) { - return asArr(l).map(fn) - } + : function(l) { + return asArr(l).map(fn); + }; } /** @@ -91,8 +92,8 @@ export function map (fn, list) { * @example * tonal.compact(['a', 'b', null, 'c']) // => ['a', 'b', 'c'] */ -export function compact (arr) { - return asArr(arr).filter(hasVal) +export function compact(arr) { + return asArr(arr).filter(hasVal); } /** @@ -108,31 +109,31 @@ export function compact (arr) { * @example * t.filter(t.noteName, 'a b c x bb') // => [ 'a', 'b', 'c', 'bb' ] */ -export function filter (fn, list) { +export function filter(fn, list) { return arguments.length > 1 ? filter(fn)(list) - : function (l) { - return asArr(l).filter(fn) - } + : function(l) { + return asArr(l).filter(fn); + }; } // a custom height function that // - returns -Infinity for non-pitch objects // - assumes pitch classes has octave -100 (so are sorted before that notes) -function objHeight (p) { - if (!p) return -Infinity - var f = fifths(p) * 7 - var o = focts(p) || -Math.floor(f / 12) - 100 - return f + o * 12 +function objHeight(p) { + if (!p) return -Infinity; + var f = fifths(p) * 7; + var o = focts(p) || -Math.floor(f / 12) - 100; + return f + o * 12; } // ascending comparator -function ascComp (a, b) { - return objHeight(a) - objHeight(b) +function ascComp(a, b) { + return objHeight(a) - objHeight(b); } // descending comparator -function descComp (a, b) { - return -ascComp(a, b) +function descComp(a, b) { + return -ascComp(a, b); } /** @@ -153,15 +154,16 @@ function descComp (a, b) { * // if is not a note, it wil be removed * array.sort('g h f i c') // => ['C', 'F', 'G'] */ -export function sort (list, comp) { - var fn = arguments.length === 1 || comp === true - ? ascComp - : comp === false ? descComp : typeof comp === 'function' ? comp : ascComp +export function sort(list, comp) { + var fn = + arguments.length === 1 || comp === true + ? ascComp + : comp === false ? descComp : typeof comp === "function" ? comp : ascComp; // if the list is an array, make a copy - list = Array.isArray(list) ? list.slice() : asArr(list) - return listFn(function (arr) { - return arr.sort(fn).filter(hasVal) - }, list) + list = Array.isArray(list) ? list.slice() : asArr(list); + return listFn(function(arr) { + return arr.sort(fn).filter(hasVal); + }, list); } /** @@ -177,20 +179,20 @@ export function sort (list, comp) { * var tonal = require('tonal') * tonal.shuffle('C D E F') */ -export var shuffle = listFn(function (arr) { - var i, t - var m = arr.length +export var shuffle = listFn(function(arr) { + var i, t; + var m = arr.length; while (m) { - i = (Math.random() * m--) | 0 - t = arr[m] - arr[m] = arr[i] - arr[i] = t + i = (Math.random() * m--) | 0; + t = arr[m]; + arr[m] = arr[i]; + arr[i] = t; } - return arr -}) + return arr; +}); -function trOct (n) { - return tr(pitch(0, n, 1)) +function trOct(n) { + return tr(pitch(0, n, 1)); } /** @@ -200,11 +202,11 @@ function trOct (n) { * @param {Array|String} list - the list to be rotated * @return {Array} the rotated array */ -export function rotate (times, list) { - var arr = asArr(list) - var len = arr.length - var n = (times % len + len) % len - return arr.slice(n, len).concat(arr.slice(0, n)) +export function rotate(times, list) { + var arr = asArr(list); + var len = arr.length; + var n = (times % len + len) % len; + return arr.slice(n, len).concat(arr.slice(0, n)); } /** @@ -217,21 +219,21 @@ export function rotate (times, list) { * @param {Array|String} list - the list to be rotated * @return {Array} the rotated array */ -export function rotateAsc (times, list) { - return listFn(function (arr) { - var len = arr.length - var n = (times % len + len) % len - var head = arr.slice(n, len) - var tail = arr.slice(0, n) +export function rotateAsc(times, list) { + return listFn(function(arr) { + var len = arr.length; + var n = (times % len + len) % len; + var head = arr.slice(n, len); + var tail = arr.slice(0, n); // See if the first note of tail is lower than the last of head - var s = semitones(head[len - n - 1], tail[0]) + var s = semitones(head[len - n - 1], tail[0]); if (s < 0) { - var octs = Math.floor(s / 12) - if (times < 0) head = head.map(trOct(octs)) - else tail = tail.map(trOct(-octs)) + var octs = Math.floor(s / 12); + if (times < 0) head = head.map(trOct(octs)); + else tail = tail.map(trOct(-octs)); } - return head.concat(tail) - }, list) + return head.concat(tail); + }, list); } /** @@ -246,16 +248,16 @@ export function rotateAsc (times, list) { * select('1 3 5', 'C D E F G A B') // => ['C', 'E', 'G'] * select('-1 0 1 2 3', 'C D') // => [ null, null, 'C', 'D', null ] */ -export function select (nums, list) { +export function select(nums, list) { if (arguments.length === 1) { - return function (l) { - return select(nums, l) - } + return function(l) { + return select(nums, l); + }; } - var arr = asArr(list) - return asArr(nums).map(function (n) { - return arr[n - 1] || null - }) + var arr = asArr(list); + return asArr(nums).map(function(n) { + return arr[n - 1] || null; + }); } // http://stackoverflow.com/questions/9960908/permutations-in-javascript @@ -264,26 +266,26 @@ export function select (nums, list) { * @param {Array|Strng} list - the list * @return {Array} an array with all the permutations */ -export function permutations (list) { - list = asArr(list) - if (list.length === 0) return [[]] - return permutations(list.slice(1)).reduce(function (acc, perm) { +export function permutations(list) { + list = asArr(list); + if (list.length === 0) return [[]]; + return permutations(list.slice(1)).reduce(function(acc, perm) { return acc.concat( - list.map(function (e, pos) { - var newPerm = perm.slice() - newPerm.splice(pos, 0, list[0]) - return newPerm + list.map(function(e, pos) { + var newPerm = perm.slice(); + newPerm.splice(pos, 0, list[0]); + return newPerm; }) - ) - }, []) + ); + }, []); } // #### Transform lists in array notation -function asPitchStr (p) { - return strPitch(p) || p +function asPitchStr(p) { + return strPitch(p) || p; } -function listToStr (v) { - return isPitch(v) ? strPitch(v) : isArr(v) ? v.map(asPitchStr) : v +function listToStr(v) { + return isPitch(v) ? strPitch(v) : isArr(v) ? v.map(asPitchStr) : v; } /** @@ -299,13 +301,13 @@ function listToStr (v) { * var octUp = listFn((p) => { p[2] = p[2] + 1; return p[2] }) * octUp('C2 D2 E2') // => ['C3', 'D3', 'E3'] */ -function listFn (fn, list) { +function listFn(fn, list) { if (arguments.length === 1) { - return function (l) { - return listFn(fn, l) - } + return function(l) { + return listFn(fn, l); + }; } - var arr = asArr(list).map(asPitch) - var res = fn(arr) - return listToStr(res) + var arr = asArr(list).map(asPitch); + var res = fn(arr); + return listToStr(res); } diff --git a/packages/core/array/test/array.test.js b/packages/core/array/test/array.test.js index e10f1d6..16096d8 100644 --- a/packages/core/array/test/array.test.js +++ b/packages/core/array/test/array.test.js @@ -1,97 +1,136 @@ /* global describe it expect */ -var _ = require('../') -function up (s) { return s.toUpperCase() } +var _ = require("../"); +function up(s) { + return s.toUpperCase(); +} -describe('tonal-array', () => { - it('asArr', () => { - expect(_.asArr('a b c')).toEqual([ 'a', 'b', 'c' ]) - expect(_.asArr('a | b | c ')).toEqual(['a', 'b', 'c']) - expect(_.asArr('a , b | c d')).toEqual(['a', 'b', 'c', 'd']) - }) +describe("tonal-array", () => { + it("asArr", () => { + expect(_.asArr("a b c")).toEqual(["a", "b", "c"]); + expect(_.asArr("a | b | c ")).toEqual(["a", "b", "c"]); + expect(_.asArr("a , b | c d")).toEqual(["a", "b", "c", "d"]); + }); - it('map', () => { - expect(_.map(up, 'a bb cx')).toEqual([ 'A', 'BB', 'CX' ]) - var ups = _.map(up) - expect(ups('a bb cx')).toEqual([ 'A', 'BB', 'CX' ]) - }) + it("map", () => { + expect(_.map(up, "a bb cx")).toEqual(["A", "BB", "CX"]); + var ups = _.map(up); + expect(ups("a bb cx")).toEqual(["A", "BB", "CX"]); + }); - describe('sort', () => { - it('sort notes', () => { - expect(_.sort('c4 c3 c2')).toEqual([ 'C2', 'C3', 'C4' ]) - expect(_.sort('c2 c3 c4', false)).toEqual([ 'C4', 'C3', 'C2' ]) - }) - it('sort pitch classes', () => { - expect(_.sort('a b c d e f g')).toEqual([ 'C', 'D', 'E', 'F', 'G', 'A', 'B' ]) - }) - it('sort intervals', () => { - expect(_.sort('2m 1P 3m 3M 4A 4P 9m 8P')).toEqual([ '1P', '2m', '3m', '3M', '4P', '4A', '8P', '9m' ]) - expect(_.sort('2m 1P 3m 3M 4A 4P 9m 8P', false)).toEqual([ '9m', '8P', '4A', '4P', '3M', '3m', '2m', '1P' ]) - }) - it('pitch classes are sorted before notes', () => { - expect(_.sort('c3 d2 d c')).toEqual([ 'C', 'D', 'D2', 'C3' ]) - }) - it('removes anything that is not a pitch', () => { - expect(_.sort('a2 g3 f h c n x')).toEqual([ 'C', 'F', 'A2', 'G3' ]) - expect(_.sort('a2 g3 f h c n x', false)).toEqual([ 'G3', 'A2', 'F', 'C' ]) - }) - it('not modifies the array', () => { - var arr = ['c2', 'c1', 'c0'] - expect(_.sort(arr)).toEqual(['C0', 'C1', 'C2']) - expect(arr).toEqual(['c2', 'c1', 'c0']) - }) - }) + describe("sort", () => { + it("sort notes", () => { + expect(_.sort("c4 c3 c2")).toEqual(["C2", "C3", "C4"]); + expect(_.sort("c2 c3 c4", false)).toEqual(["C4", "C3", "C2"]); + }); + it("sort pitch classes", () => { + expect(_.sort("a b c d e f g")).toEqual([ + "C", + "D", + "E", + "F", + "G", + "A", + "B" + ]); + }); + it("sort intervals", () => { + expect(_.sort("2m 1P 3m 3M 4A 4P 9m 8P")).toEqual([ + "1P", + "2m", + "3m", + "3M", + "4P", + "4A", + "8P", + "9m" + ]); + expect(_.sort("2m 1P 3m 3M 4A 4P 9m 8P", false)).toEqual([ + "9m", + "8P", + "4A", + "4P", + "3M", + "3m", + "2m", + "1P" + ]); + }); + it("pitch classes are sorted before notes", () => { + expect(_.sort("c3 d2 d c")).toEqual(["C", "D", "D2", "C3"]); + }); + it("removes anything that is not a pitch", () => { + expect(_.sort("a2 g3 f h c n x")).toEqual(["C", "F", "A2", "G3"]); + expect(_.sort("a2 g3 f h c n x", false)).toEqual(["G3", "A2", "F", "C"]); + }); + it("not modifies the array", () => { + var arr = ["c2", "c1", "c0"]; + expect(_.sort(arr)).toEqual(["C0", "C1", "C2"]); + expect(arr).toEqual(["c2", "c1", "c0"]); + }); + }); - it('compact', () => { - expect(_.compact(['a', null, 'b'])).toEqual(['a', 'b']) - expect(_.compact([0, 1, 2, 3, null, 4])).toEqual([ 0, 1, 2, 3, 4 ]) - }) + it("compact", () => { + expect(_.compact(["a", null, "b"])).toEqual(["a", "b"]); + expect(_.compact([0, 1, 2, 3, null, 4])).toEqual([0, 1, 2, 3, 4]); + }); - it('filter', () => { - function isUpLetter (s) { return 'CDEFGAB'.indexOf(s[0]) !== -1 } - expect(_.filter(isUpLetter, 'C d f4 A4 M3')).toEqual([ 'C', 'A4' ]) - }) + it("filter", () => { + function isUpLetter(s) { + return "CDEFGAB".indexOf(s[0]) !== -1; + } + expect(_.filter(isUpLetter, "C d f4 A4 M3")).toEqual(["C", "A4"]); + }); - it('shuffle', () => { - var s = _.shuffle('A B C D') - expect(s.length).toBe(4) - expect(s.indexOf('A')).not.toBe(-1) - expect(s.indexOf('B')).not.toBe(-1) - expect(s.indexOf('C')).not.toBe(-1) - expect(s.indexOf('D')).not.toBe(-1) - }) + it("shuffle", () => { + var s = _.shuffle("A B C D"); + expect(s.length).toBe(4); + expect(s.indexOf("A")).not.toBe(-1); + expect(s.indexOf("B")).not.toBe(-1); + expect(s.indexOf("C")).not.toBe(-1); + expect(s.indexOf("D")).not.toBe(-1); + }); - it('rotate', () => { - expect(_.rotate(1, 'c d e')).toEqual(['d', 'e', 'c']) - expect(_.rotate(-1, 'c d e')).toEqual([ 'e', 'c', 'd' ]) - expect(_.rotate(0, 'c d e')).toEqual([ 'c', 'd', 'e' ]) - }) + it("rotate", () => { + expect(_.rotate(1, "c d e")).toEqual(["d", "e", "c"]); + expect(_.rotate(-1, "c d e")).toEqual(["e", "c", "d"]); + expect(_.rotate(0, "c d e")).toEqual(["c", "d", "e"]); + }); - it('rotateAsc', () => { - expect(_.rotateAsc(1, 'c d e')).toEqual(['D', 'E', 'C']) - expect(_.rotateAsc(-1, 'c d e')).toEqual([ 'E', 'C', 'D' ]) - expect(_.rotateAsc(0, 'c d e')).toEqual([ 'C', 'D', 'E' ]) - expect(_.rotateAsc(1, 'c4 d4 e4')).toEqual([ 'D4', 'E4', 'C5' ]) - expect(_.rotateAsc(2, 'c4 d4 e4')).toEqual([ 'E4', 'C5', 'D5' ]) - expect(_.rotateAsc(-1, 'c4 d4 e4')).toEqual([ 'E3', 'C4', 'D4' ]) - expect(_.rotateAsc(-2, 'c4 d4 e4')).toEqual([ 'D3', 'E3', 'C4' ]) - expect(_.rotateAsc(1, 'C1 D3 E5')).toEqual([ 'D3', 'E5', 'C6' ]) - expect(_.rotateAsc(2, 'C1 D3 E5')).toEqual([ 'E5', 'C6', 'D8' ]) - expect(_.rotateAsc(-1, 'C1 D3 E5')).toEqual([ 'E0', 'C1', 'D3' ]) - expect(_.rotateAsc(-2, 'C1 D3 E5')).toEqual([ 'D-2', 'E0', 'C1' ]) - }) + it("rotateAsc", () => { + expect(_.rotateAsc(1, "c d e")).toEqual(["D", "E", "C"]); + expect(_.rotateAsc(-1, "c d e")).toEqual(["E", "C", "D"]); + expect(_.rotateAsc(0, "c d e")).toEqual(["C", "D", "E"]); + expect(_.rotateAsc(1, "c4 d4 e4")).toEqual(["D4", "E4", "C5"]); + expect(_.rotateAsc(2, "c4 d4 e4")).toEqual(["E4", "C5", "D5"]); + expect(_.rotateAsc(-1, "c4 d4 e4")).toEqual(["E3", "C4", "D4"]); + expect(_.rotateAsc(-2, "c4 d4 e4")).toEqual(["D3", "E3", "C4"]); + expect(_.rotateAsc(1, "C1 D3 E5")).toEqual(["D3", "E5", "C6"]); + expect(_.rotateAsc(2, "C1 D3 E5")).toEqual(["E5", "C6", "D8"]); + expect(_.rotateAsc(-1, "C1 D3 E5")).toEqual(["E0", "C1", "D3"]); + expect(_.rotateAsc(-2, "C1 D3 E5")).toEqual(["D-2", "E0", "C1"]); + }); - it('select', () => { - expect(_.select('1 3 5', 'C D E F G A B')).toEqual(['C', 'E', 'G']) - expect(_.select('1 -3 12 4', 'C D E F G A B')).toEqual([ 'C', null, null, 'F' ]) - expect(_.select('-1 0 1 2 3', 'C D')).toEqual([ null, null, 'C', 'D', null ]) + it("select", () => { + expect(_.select("1 3 5", "C D E F G A B")).toEqual(["C", "E", "G"]); + expect(_.select("1 -3 12 4", "C D E F G A B")).toEqual([ + "C", + null, + null, + "F" + ]); + expect(_.select("-1 0 1 2 3", "C D")).toEqual([null, null, "C", "D", null]); // partial - expect(_.select('1 3')('C D E')).toEqual(['C', 'E']) - }) + expect(_.select("1 3")("C D E")).toEqual(["C", "E"]); + }); - it('permutations', () => { - expect(_.permutations('a b c')).toEqual([ - [ 'a', 'b', 'c' ], [ 'b', 'a', 'c' ], [ 'b', 'c', 'a' ], - [ 'a', 'c', 'b' ], [ 'c', 'a', 'b' ], [ 'c', 'b', 'a' ] - ]) - }) -}) + it("permutations", () => { + expect(_.permutations("a b c")).toEqual([ + ["a", "b", "c"], + ["b", "a", "c"], + ["b", "c", "a"], + ["a", "c", "b"], + ["c", "a", "b"], + ["c", "b", "a"] + ]); + }); +}); diff --git a/packages/core/dictionary/index.js b/packages/core/dictionary/index.js index 941b7b1..ba1811f 100644 --- a/packages/core/dictionary/index.js +++ b/packages/core/dictionary/index.js @@ -1,6 +1,6 @@ -import { map, compact, sort } from 'tonal-array' -import { pc } from 'tonal-note' -import { chroma, modes } from 'tonal-pcset' +import { map, compact, sort } from "tonal-array"; +import { pc } from "tonal-note"; +import { chroma, modes } from "tonal-pcset"; /** * This module contains functions to query tonal dictionaries. @@ -16,7 +16,9 @@ import { chroma, modes } from 'tonal-pcset' * * @module dictionary */ -function id (x) { return x } +function id(x) { + return x; +} /** * Create a tonal dictionary. A dictionary is an object with two functions: get and @@ -42,21 +44,21 @@ function id (x) { return x } * chords.keys() // => ['maj7', 'm7'] * chords.keys(true) // => ['maj7', 'm7', 'Maj7'] */ -export function dictionary (raw, parse) { - parse = parse || id - var byKey = {} - var names = Object.keys(raw) - var aliases = [] - names.forEach(function (k) { - var value = parse(raw[k][0]) - byKey[k] = value +export function dictionary(raw, parse) { + parse = parse || id; + var byKey = {}; + var names = Object.keys(raw); + var aliases = []; + names.forEach(function(k) { + var value = parse(raw[k][0]); + byKey[k] = value; if (raw[k][1]) { - raw[k][1].forEach(function (alias) { - byKey[alias] = value - aliases.push(alias) - }) + raw[k][1].forEach(function(alias) { + byKey[alias] = value; + aliases.push(alias); + }); } - }) + }); return { /** * Get a value by key @@ -66,7 +68,9 @@ export function dictionary (raw, parse) { * @return {Object} the value (normally an array of intervals or notes) * @memberof dictionary */ - get: function (n) { return byKey[n] }, + get: function(n) { + return byKey[n]; + }, /** * Get the valid keys of dictionary * @name keys @@ -77,12 +81,15 @@ export function dictionary (raw, parse) { * @return {Array} the keys * @memberof dictionary */ - keys: function (all, filter) { - var keys = all ? names.concat(aliases) : names.slice() - return typeof filter !== 'function' ? keys - : keys.filter(function (k) { return filter(k, byKey[k]) }) + keys: function(all, filter) { + var keys = all ? names.concat(aliases) : names.slice(); + return typeof filter !== "function" + ? keys + : keys.filter(function(k) { + return filter(k, byKey[k]); + }); } - } + }; } /** @@ -99,24 +106,26 @@ export function dictionary (raw, parse) { * var detect = detector(dictionary(DATA), '') * detect('c d e b') // => 'Cmaj/' */ -export function detector (dict, build) { - var isSep = typeof build === 'string' - var isFn = typeof build === 'function' - var nameByChroma = dict.keys(false).reduce(function (map, key) { - map[chroma(dict.get(key))] = key - return map - }, {}) +export function detector(dict, build) { + var isSep = typeof build === "string"; + var isFn = typeof build === "function"; + var nameByChroma = dict.keys(false).reduce(function(map, key) { + map[chroma(dict.get(key))] = key; + return map; + }, {}); - return function (notes) { - notes = sort(map(pc, notes)) - var sets = modes(notes) - return compact(sets.map(function (set, i) { - var type = nameByChroma[set] - if (!type) return null - var tonic = notes[i] - return isSep ? tonic + build + type - : isFn ? build(type, tonic) - : [type, tonic] - })) - } + return function(notes) { + notes = sort(map(pc, notes)); + var sets = modes(notes); + return compact( + sets.map(function(set, i) { + var type = nameByChroma[set]; + if (!type) return null; + var tonic = notes[i]; + return isSep + ? tonic + build + type + : isFn ? build(type, tonic) : [type, tonic]; + }) + ); + }; } diff --git a/packages/core/dictionary/test/dictionary.test.js b/packages/core/dictionary/test/dictionary.test.js index 589ee23..0d7b2ea 100644 --- a/packages/core/dictionary/test/dictionary.test.js +++ b/packages/core/dictionary/test/dictionary.test.js @@ -1,46 +1,48 @@ /* global describe test expect */ -var d = require('..') +var d = require(".."); var DATA = { - 'maj7': ['1P 3M 5P 7M', ['Maj7']], - 'm7': ['1P 3m 5P 7m'] + maj7: ["1P 3M 5P 7M", ["Maj7"]], + m7: ["1P 3m 5P 7m"] +}; +function split(str) { + return str.split(" "); } -function split (str) { return str.split(' ') } -describe('tonal-dictionary', () => { - test('detector', () => { - var dict = d.dictionary(DATA, split) - expect(d.detector(dict, null)('E4 C4 B2 G5')).toEqual([ ['maj7', 'C'] ]) - expect(d.detector(dict, '')('D4 b7 f#2 G5')).toEqual([ 'Gmaj7' ]) - expect(d.detector(dict, ' ')('E C5 B G3')).toEqual([ 'C maj7' ]) - }) +describe("tonal-dictionary", () => { + test("detector", () => { + var dict = d.dictionary(DATA, split); + expect(d.detector(dict, null)("E4 C4 B2 G5")).toEqual([["maj7", "C"]]); + expect(d.detector(dict, "")("D4 b7 f#2 G5")).toEqual(["Gmaj7"]); + expect(d.detector(dict, " ")("E C5 B G3")).toEqual(["C maj7"]); + }); - test('create a dictionary', () => { - var DATA = { 'M': ['1P 3M 5P'] } - expect(d.dictionary(DATA).get('M')).toEqual('1P 3M 5P') - expect(d.dictionary(DATA, split).get('M')).toEqual(['1P', '3M', '5P']) - }) + test("create a dictionary", () => { + var DATA = { M: ["1P 3M 5P"] }; + expect(d.dictionary(DATA).get("M")).toEqual("1P 3M 5P"); + expect(d.dictionary(DATA, split).get("M")).toEqual(["1P", "3M", "5P"]); + }); - test('get', () => { - var get = d.dictionary(DATA, split).get - expect(get('maj7')).toEqual([ '1P', '3M', '5P', '7M' ]) - expect(get('Maj7')).toEqual([ '1P', '3M', '5P', '7M' ]) - expect(get('m7')).toEqual([ '1P', '3m', '5P', '7m' ]) - expect(get('blah')).toBe(undefined) - }) + test("get", () => { + var get = d.dictionary(DATA, split).get; + expect(get("maj7")).toEqual(["1P", "3M", "5P", "7M"]); + expect(get("Maj7")).toEqual(["1P", "3M", "5P", "7M"]); + expect(get("m7")).toEqual(["1P", "3m", "5P", "7m"]); + expect(get("blah")).toBe(undefined); + }); - test('keys', () => { - var keys = d.dictionary(DATA, split).keys - expect(keys()).toEqual([ 'maj7', 'm7' ]) - expect(keys(true)).toEqual([ 'maj7', 'm7', 'Maj7' ]) - }) + test("keys", () => { + var keys = d.dictionary(DATA, split).keys; + expect(keys()).toEqual(["maj7", "m7"]); + expect(keys(true)).toEqual(["maj7", "m7", "Maj7"]); + }); - test('dictionry: keys with filter', () => { - var keys = d.dictionary(DATA, split).keys - var filter = function (name, intervals) { - return intervals[1] === '3M' - } - expect(keys(false, filter)).toEqual([ 'maj7' ]) - expect(keys(true, filter)).toEqual([ 'maj7', 'Maj7' ]) - }) -}) + test("dictionry: keys with filter", () => { + var keys = d.dictionary(DATA, split).keys; + var filter = function(name, intervals) { + return intervals[1] === "3M"; + }; + expect(keys(false, filter)).toEqual(["maj7"]); + expect(keys(true, filter)).toEqual(["maj7", "Maj7"]); + }); +}); diff --git a/packages/core/distance/index.js b/packages/core/distance/index.js index 3320232..39f0d61 100644 --- a/packages/core/distance/index.js +++ b/packages/core/distance/index.js @@ -13,16 +13,24 @@ * * @module distance */ -import { isPC, fifths, focts, pitch, height, asPitch, strIvl } from 'tonal-pitch' +import { + isPC, + fifths, + focts, + pitch, + height, + asPitch, + strIvl +} from "tonal-pitch"; // substract two pitches -function substr (a, b) { - if (!a || !b || a[1].length !== b[1].length) return null - var f = fifths(b) - fifths(a) - if (isPC(a)) return pitch(f, -Math.floor(f * 7 / 12), 1) - var o = focts(b) - focts(a) - var d = height(b) - height(a) < 0 ? -1 : 1 - return pitch(d * f, d * o, d) +function substr(a, b) { + if (!a || !b || a[1].length !== b[1].length) return null; + var f = fifths(b) - fifths(a); + if (isPC(a)) return pitch(f, -Math.floor(f * 7 / 12), 1); + var o = focts(b) - focts(a); + var d = height(b) - height(a) < 0 ? -1 : 1; + return pitch(d * f, d * o, d); } /** @@ -44,13 +52,16 @@ function substr (a, b) { * var tonal = require('tonal') * tonal.distance.interval('M2', 'P5') // => 'P4' */ -export function interval (a, b) { - if (arguments.length === 1) return function (b) { return interval(a, b) } - var pa = asPitch(a) - var pb = asPitch(b) - var i = substr(pa, pb) +export function interval(a, b) { + if (arguments.length === 1) + return function(b) { + return interval(a, b); + }; + var pa = asPitch(a); + var pb = asPitch(b); + var i = substr(pa, pb); // if a and b are in array notation, no conversion back - return a === pa && b === pb ? i : strIvl(i) + return a === pa && b === pb ? i : strIvl(i); } /** @@ -64,7 +75,7 @@ export function interval (a, b) { * // or use tonal * tonal.distance.semitones('C3', 'G3') // => 7 */ -export function semitones (a, b) { - var i = substr(asPitch(a), asPitch(b)) - return i ? height(i) : null +export function semitones(a, b) { + var i = substr(asPitch(a), asPitch(b)); + return i ? height(i) : null; } diff --git a/packages/core/distance/test/distance.test.js b/packages/core/distance/test/distance.test.js index a197e76..4d18eb1 100644 --- a/packages/core/distance/test/distance.test.js +++ b/packages/core/distance/test/distance.test.js @@ -1,43 +1,74 @@ /* global describe test expect */ -var dist = require('..') +var dist = require(".."); -var map = function (fn, s) { - if (arguments.length === 1) return function (s) { return map(fn, s) } - return (Array.isArray(s) ? s : s.split(' ')).map(fn) -} +var map = function(fn, s) { + if (arguments.length === 1) + return function(s) { + return map(fn, s); + }; + return (Array.isArray(s) ? s : s.split(" ")).map(fn); +}; -describe('tonal-distance', () => { - test('get distance between notes', () => { - var fromC3 = map(dist.interval('C3')) - expect(fromC3('C3 e3 e4 c2 e2')).toEqual([ '1P', '3M', '10M', '-8P', '-6m' ]) - }) +describe("tonal-distance", () => { + test("get distance between notes", () => { + var fromC3 = map(dist.interval("C3")); + expect(fromC3("C3 e3 e4 c2 e2")).toEqual(["1P", "3M", "10M", "-8P", "-6m"]); + }); - test('distances between pitch classes are always ascending', () => { - expect(dist.interval('C', 'D')).toEqual('2M') - var fromC = map(dist.interval('C')) - expect(fromC('c d e f g a b')).toEqual([ '1P', '2M', '3M', '4P', '5P', '6M', '7M' ]) - var fromG = map(dist.interval('G')) - expect(fromG('c d e f g a b')).toEqual([ '4P', '5P', '6M', '7m', '1P', '2M', '3M' ]) - }) - test('get difference between intervals', () => { - var subsM2 = map(dist.interval('M2')) - expect(subsM2('P1 M2 M3 P4 P5 M6 M7')).toEqual([ '-2M', '1P', '2M', '3m', '4P', '5P', '6M' ]) - }) - test('pitch types can not be mixed', () => { - expect(dist.interval('C', 'C2')).toBe(null) - expect(dist.interval('C2', 'C')).toBe(null) - }) - test('when both pitches are in arra format, return array format', () => { - expect(dist.interval(['tnlp', [0, 0]], ['tnlp', [1, 0]])).toEqual([ 'tnlp', [1, 0], 1 ]) - }) - test('when one pitch in array format, return string', () => { - expect(dist.interval(['tnlp', [0]], 'D')).toEqual('2M') - }) + test("distances between pitch classes are always ascending", () => { + expect(dist.interval("C", "D")).toEqual("2M"); + var fromC = map(dist.interval("C")); + expect(fromC("c d e f g a b")).toEqual([ + "1P", + "2M", + "3M", + "4P", + "5P", + "6M", + "7M" + ]); + var fromG = map(dist.interval("G")); + expect(fromG("c d e f g a b")).toEqual([ + "4P", + "5P", + "6M", + "7m", + "1P", + "2M", + "3M" + ]); + }); + test("get difference between intervals", () => { + var subsM2 = map(dist.interval("M2")); + expect(subsM2("P1 M2 M3 P4 P5 M6 M7")).toEqual([ + "-2M", + "1P", + "2M", + "3m", + "4P", + "5P", + "6M" + ]); + }); + test("pitch types can not be mixed", () => { + expect(dist.interval("C", "C2")).toBe(null); + expect(dist.interval("C2", "C")).toBe(null); + }); + test("when both pitches are in arra format, return array format", () => { + expect(dist.interval(["tnlp", [0, 0]], ["tnlp", [1, 0]])).toEqual([ + "tnlp", + [1, 0], + 1 + ]); + }); + test("when one pitch in array format, return string", () => { + expect(dist.interval(["tnlp", [0]], "D")).toEqual("2M"); + }); - test('semitones', () => { - expect(dist.semitones('C3', 'G#3')).toBe(8) - expect(dist.semitones('C4', 'A3')).toBe(-3) - expect(dist.semitones('blah', 'C3')).toBe(null) - expect(dist.semitones('C', 'D')).toBe(2) - }) -}) + test("semitones", () => { + expect(dist.semitones("C3", "G#3")).toBe(8); + expect(dist.semitones("C4", "A3")).toBe(-3); + expect(dist.semitones("blah", "C3")).toBe(null); + expect(dist.semitones("C", "D")).toBe(2); + }); +}); diff --git a/packages/core/encoding/index.js b/packages/core/encoding/index.js index 4513109..cef1cea 100644 --- a/packages/core/encoding/index.js +++ b/packages/core/encoding/index.js @@ -6,15 +6,19 @@ * @module encoding */ -function isNum (n) { return typeof n === 'number' } +function isNum(n) { + return typeof n === "number"; +} // 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] +var FIFTHS = [0, 2, 4, -1, 1, 3, 5]; // Given a number of fifths, return the octaves they span -function fOcts (f) { return Math.floor(f * 7 / 12) } +function fOcts(f) { + return Math.floor(f * 7 / 12); +} // Get the number of octaves it span each step -var FIFTH_OCTS = FIFTHS.map(fOcts) +var FIFTH_OCTS = FIFTHS.map(fOcts); /** * Given a note's step, alteration and octave returns the fiths/octave @@ -24,24 +28,24 @@ var FIFTH_OCTS = FIFTHS.map(fOcts) * @param {number} octave - the note octave * @return {Array} the [fifths, octave] representation of that note */ -export function encode (step, alt, oct) { - var f = FIFTHS[step] + 7 * alt - if (!isNum(oct)) return [f] - var o = oct - FIFTH_OCTS[step] - 4 * alt - return [f, o] +export function encode(step, alt, oct) { + var f = FIFTHS[step] + 7 * alt; + if (!isNum(oct)) return [f]; + var o = oct - FIFTH_OCTS[step] - 4 * alt; + return [f, o]; } // Return the number of fifths as if it were unaltered -function unaltered (f) { - var i = (f + 1) % 7 - return i < 0 ? 7 + i : i +function unaltered(f) { + var i = (f + 1) % 7; + return i < 0 ? 7 + i : i; } // 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] +var STEPS = [3, 0, 4, 1, 5, 2, 6]; /** * Decode a encoded pitch @@ -49,10 +53,10 @@ var STEPS = [3, 0, 4, 1, 5, 2, 6] * @param {Number} octs - the number of octaves to compensate the fifhts * @return {Array} in the form [step, alt, oct] */ -export function decode (f, o) { - var step = STEPS[unaltered(f)] - var alt = Math.floor((f + 1) / 7) - if (!isNum(o)) return [step, alt] - var oct = o + 4 * alt + FIFTH_OCTS[step] - return [step, alt, oct] +export function decode(f, o) { + var step = STEPS[unaltered(f)]; + var alt = Math.floor((f + 1) / 7); + if (!isNum(o)) return [step, alt]; + var oct = o + 4 * alt + FIFTH_OCTS[step]; + return [step, alt, oct]; } diff --git a/packages/core/encoding/test/encoding.test.js b/packages/core/encoding/test/encoding.test.js index c9ee191..23b9292 100644 --- a/packages/core/encoding/test/encoding.test.js +++ b/packages/core/encoding/test/encoding.test.js @@ -1,41 +1,73 @@ /* global describe it expect */ -var n = require('..') +var n = require(".."); -describe('tonal-encoding', () => { - it('encode pitch classes', () => { - expect([0, 1, 2, 3, 4, 5, 6].map(function (e) { return n.encode(e, 0) })) - .toEqual([ [ 0 ], [ 2 ], [ 4 ], [ -1 ], [ 1 ], [ 3 ], [ 5 ] ]) - expect([0, 1, 2, 3, 4, 5, 6].map(function (e) { return n.encode(e, 1) })) - .toEqual([ [ 7 ], [ 9 ], [ 11 ], [ 6 ], [ 8 ], [ 10 ], [ 12 ] ]) - expect([0, 1, 2, 3, 4, 5, 6].map(function (e) { return n.encode(e, -1) })) - .toEqual([ [ -7 ], [ -5 ], [ -3 ], [ -8 ], [ -6 ], [ -4 ], [ -2 ] ]) - }) +describe("tonal-encoding", () => { + it("encode pitch classes", () => { + expect( + [0, 1, 2, 3, 4, 5, 6].map(function(e) { + return n.encode(e, 0); + }) + ).toEqual([[0], [2], [4], [-1], [1], [3], [5]]); + expect( + [0, 1, 2, 3, 4, 5, 6].map(function(e) { + return n.encode(e, 1); + }) + ).toEqual([[7], [9], [11], [6], [8], [10], [12]]); + expect( + [0, 1, 2, 3, 4, 5, 6].map(function(e) { + return n.encode(e, -1); + }) + ).toEqual([[-7], [-5], [-3], [-8], [-6], [-4], [-2]]); + }); - it('encode notes or intervals', () => { + it("encode notes or intervals", () => { // C#2 [0, 1, 2] - expect(n.encode(0, 1, 2)).toEqual([ 7, -2 ]) + expect(n.encode(0, 1, 2)).toEqual([7, -2]); // Db - expect(n.encode(1, -1)).toEqual([-5]) + expect(n.encode(1, -1)).toEqual([-5]); // Db-1 [1, -1, -1] - expect(n.encode(1, -1, -1)).toEqual([-5, 2]) + expect(n.encode(1, -1, -1)).toEqual([-5, 2]); // 8A - expect(n.encode(0, 1, 1)).toEqual([ 7, -3 ]) + expect(n.encode(0, 1, 1)).toEqual([7, -3]); // 9m - expect(n.encode(1, -1, 1)).toEqual([ -5, 4 ]) - }) + expect(n.encode(1, -1, 1)).toEqual([-5, 4]); + }); - it('decode pitch class', () => { - function dec (e) { return n.decode.apply(null, e) } - expect([ [ 0 ], [ 2 ], [ 4 ], [ -1 ], [ 1 ], [ 3 ], [ 5 ] ].map(dec)) - .toEqual([ [ 0, 0 ], [ 1, 0 ], [ 2, 0 ], [ 3, 0 ], [ 4, 0 ], [ 5, 0 ], [ 6, 0 ] ]) - expect([ [ 7 ], [ 9 ], [ 11 ], [ 6 ], [ 8 ], [ 10 ], [ 12 ] ].map(dec)) - .toEqual([ [ 0, 1 ], [ 1, 1 ], [ 2, 1 ], [ 3, 1 ], [ 4, 1 ], [ 5, 1 ], [ 6, 1 ] ]) - expect([ [ -7 ], [ -5 ], [ -3 ], [ -8 ], [ -6 ], [ -4 ], [ -2 ] ].map(dec)) - .toEqual([ [ 0, -1 ], [ 1, -1 ], [ 2, -1 ], [ 3, -1 ], [ 4, -1 ], [ 5, -1 ], [ 6, -1 ] ]) - }) + it("decode pitch class", () => { + function dec(e) { + return n.decode.apply(null, e); + } + expect([[0], [2], [4], [-1], [1], [3], [5]].map(dec)).toEqual([ + [0, 0], + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 0], + [6, 0] + ]); + expect([[7], [9], [11], [6], [8], [10], [12]].map(dec)).toEqual([ + [0, 1], + [1, 1], + [2, 1], + [3, 1], + [4, 1], + [5, 1], + [6, 1] + ]); + expect([[-7], [-5], [-3], [-8], [-6], [-4], [-2]].map(dec)).toEqual([ + [0, -1], + [1, -1], + [2, -1], + [3, -1], + [4, -1], + [5, -1], + [6, -1] + ]); + }); - it('decode note or intervals', () => { - expect(n.decode(7, -2)).toEqual([0, 1, 2]) - expect(n.decode(-7, 4)).toEqual([0, -1, 0]) - }) -}) + it("decode note or intervals", () => { + expect(n.decode(7, -2)).toEqual([0, 1, 2]); + expect(n.decode(-7, 4)).toEqual([0, -1, 0]); + }); +}); diff --git a/packages/core/freq/index.js b/packages/core/freq/index.js index a366e5b..d3d2323 100644 --- a/packages/core/freq/index.js +++ b/packages/core/freq/index.js @@ -23,15 +23,15 @@ * * @module freq */ -import { toMidi as noteToMidi, note as midiToNote } from 'tonal-midi' +import { toMidi as noteToMidi, note as midiToNote } from "tonal-midi"; // decorate a function to round the numeric result to a max -function round (m, fn) { - m = m || m === 0 ? Math.pow(10, m) : false - return function (v) { - v = fn(v) - return v === null ? null : m ? Math.round(v * m) / m : v - } +function round(m, fn) { + m = m || m === 0 ? Math.pow(10, m) : false; + return function(v) { + v = fn(v); + return v === null ? null : m ? Math.round(v * m) / m : v; + }; } /** @@ -48,12 +48,12 @@ function round (m, fn) { * const toFreq = eqTempFreq(444, 2) * toFreq('A3') // => 222 */ -export function eqTempFreq (ref, max, note) { - if (arguments.length > 2) return eqTempFreq(ref, max)(note) - return round(max, function (p) { - var m = noteToMidi(p) - return m ? Math.pow(2, (m - 69) / 12) * ref : null - }) +export function eqTempFreq(ref, max, note) { + if (arguments.length > 2) return eqTempFreq(ref, max)(note); + return round(max, function(p) { + var m = noteToMidi(p); + return m ? Math.pow(2, (m - 69) / 12) * ref : null; + }); } /** @@ -68,7 +68,7 @@ export function eqTempFreq (ref, max, note) { * freq.toFreq('A4') // => 440 * freq.toFreq('C4') // => 261.63 */ -export var toFreq = eqTempFreq(440, 2) +export var toFreq = eqTempFreq(440, 2); /** * Get the midi note from a frequency in equal temperament scale. You can @@ -78,11 +78,11 @@ export var toFreq = eqTempFreq(440, 2) * @param {Number} freq - the frequency * @return {Number} the midi number */ -export function eqTempFreqToMidi (ref, max, freq) { - if (arguments.length > 2) return eqTempFreqToMidi(ref, max)(freq) - return round(max, function (freq) { - return 12 * (Math.log(freq) - Math.log(ref)) / Math.log(2) + 69 - }) +export function eqTempFreqToMidi(ref, max, freq) { + if (arguments.length > 2) return eqTempFreqToMidi(ref, max)(freq); + return round(max, function(freq) { + return 12 * (Math.log(freq) - Math.log(ref)) / Math.log(2) + 69; + }); } /** @@ -96,7 +96,7 @@ export function eqTempFreqToMidi (ref, max, freq) { * @example * freq.toMidi(361) // => 59.96 */ -export var toMidi = eqTempFreqToMidi(440, 2) +export var toMidi = eqTempFreqToMidi(440, 2); /** * Get note name from frequency using an equal temperament scale with 440Hz @@ -108,8 +108,8 @@ export var toMidi = eqTempFreqToMidi(440, 2) * @example * freq.note(440) // => 'A4' */ -export function note (freq, useSharps) { - return midiToNote(toMidi(freq), useSharps) +export function note(freq, useSharps) { + return midiToNote(toMidi(freq), useSharps); } /** @@ -122,8 +122,8 @@ export function note (freq, useSharps) { * import { cents } from 'tonal-freq' * cents('C4', 261) // => -4 */ -export function cents (base, freq) { - var b = toFreq(base) || base - var f = toFreq(freq) || freq - return Math.round(1200 * (Math.log(f / b) / Math.log(2))) +export function cents(base, freq) { + var b = toFreq(base) || base; + var f = toFreq(freq) || freq; + return Math.round(1200 * (Math.log(f / b) / Math.log(2))); } diff --git a/packages/core/freq/test/freq.test.js b/packages/core/freq/test/freq.test.js index 0955964..22a31c8 100644 --- a/packages/core/freq/test/freq.test.js +++ b/packages/core/freq/test/freq.test.js @@ -1,47 +1,51 @@ /* global describe it expect */ -var freq = require('..') +var freq = require(".."); -describe('tonal-freq', () => { - it('note name to frequency', () => { - expect(freq.toFreq('A4')).toBe(440) - expect(freq.toFreq('C4')).toBe(261.63) - expect(freq.toFreq('blah')).toBe(null) - }) +describe("tonal-freq", () => { + it("note name to frequency", () => { + expect(freq.toFreq("A4")).toBe(440); + expect(freq.toFreq("C4")).toBe(261.63); + expect(freq.toFreq("blah")).toBe(null); + }); - it('midi number to frequency', () => { - expect(freq.toFreq(69)).toBe(freq.toFreq('A4')) - expect(freq.toFreq(60)).toBe(freq.toFreq('C4')) - }) + it("midi number to frequency", () => { + expect(freq.toFreq(69)).toBe(freq.toFreq("A4")); + expect(freq.toFreq(60)).toBe(freq.toFreq("C4")); + }); - it('freq: eqTempFreqToMidi', () => { - expect(freq.eqTempFreqToMidi(440, 2, 440)).toBe(69) - expect(freq.eqTempFreqToMidi(444, 2, 440)).toBe(68.84) - expect(freq.eqTempFreqToMidi(444, 4, 440)).toBe(68.8433) - expect(freq.eqTempFreqToMidi(440, 4, 255)).toBe(59.5559) - expect(freq.eqTempFreqToMidi(440, 0, 255)).toBe(60) - }) + it("freq: eqTempFreqToMidi", () => { + expect(freq.eqTempFreqToMidi(440, 2, 440)).toBe(69); + expect(freq.eqTempFreqToMidi(444, 2, 440)).toBe(68.84); + expect(freq.eqTempFreqToMidi(444, 4, 440)).toBe(68.8433); + expect(freq.eqTempFreqToMidi(440, 4, 255)).toBe(59.5559); + expect(freq.eqTempFreqToMidi(440, 0, 255)).toBe(60); + }); - it('freq: toMidi', () => { - expect(freq.toMidi(220)).toBe(57) - expect(freq.toMidi(261.62)).toBe(60) - expect(freq.toMidi(261)).toBe(59.96) - }) + it("freq: toMidi", () => { + expect(freq.toMidi(220)).toBe(57); + expect(freq.toMidi(261.62)).toBe(60); + expect(freq.toMidi(261)).toBe(59.96); + }); - it('note name from frequency', () => { - expect(freq.note(261)).toBe('C4') - expect(freq.note(275)).toBe('Db4') - expect(freq.note(275, true)).toBe('C#4') - }) + it("note name from frequency", () => { + expect(freq.note(261)).toBe("C4"); + expect(freq.note(275)).toBe("Db4"); + expect(freq.note(275, true)).toBe("C#4"); + }); - it('get distance in cents', () => { - expect(freq.cents(261, 'C4')).toBe(4) - expect(freq.cents('C4', 261)).toBe(-4) - }) + it("get distance in cents", () => { + expect(freq.cents(261, "C4")).toBe(4); + expect(freq.cents("C4", 261)).toBe(-4); + }); - it('freq: eqTempFreq', () => { - expect(freq.eqTempFreq(444, 4, 'C6')).toBe(1056.0159) - expect(freq.eqTempFreq(444, 3, 'C6')).toBe(1056.016) - expect('c4 d4 e4 f4 g4 a4 b4'.split(' ').map(freq.eqTempFreq(440, 1))).toEqual([ 261.6, 293.7, 329.6, 349.2, 392, 440, 493.9 ]) - expect('c4 d4 e4 f4 g4 a4 b4'.split(' ').map(freq.eqTempFreq(440, 2))).toEqual([ 261.63, 293.66, 329.63, 349.23, 392, 440, 493.88 ]) - }) -}) + it("freq: eqTempFreq", () => { + expect(freq.eqTempFreq(444, 4, "C6")).toBe(1056.0159); + expect(freq.eqTempFreq(444, 3, "C6")).toBe(1056.016); + expect( + "c4 d4 e4 f4 g4 a4 b4".split(" ").map(freq.eqTempFreq(440, 1)) + ).toEqual([261.6, 293.7, 329.6, 349.2, 392, 440, 493.9]); + expect( + "c4 d4 e4 f4 g4 a4 b4".split(" ").map(freq.eqTempFreq(440, 2)) + ).toEqual([261.63, 293.66, 329.63, 349.23, 392, 440, 493.88]); + }); +}); diff --git a/packages/core/harmonizer/index.js b/packages/core/harmonizer/index.js index 126b9ea..070840f 100644 --- a/packages/core/harmonizer/index.js +++ b/packages/core/harmonizer/index.js @@ -23,9 +23,9 @@ * * @module harmonizer */ -import { transpose as tr } from 'tonal-transpose' -import { interval } from 'tonal-distance' -import { asArr, map, compact } from 'tonal-array' +import { transpose as tr } from "tonal-transpose"; +import { interval } from "tonal-distance"; +import { asArr, map, compact } from "tonal-array"; /** * Given a list of notes, return the distance from the first note to the rest. @@ -38,9 +38,9 @@ import { asArr, map, compact } from 'tonal-array' * // in tonal this functions are NOT namespaced * tonal.harmonics(tonal.scale('C major')) // => ['1P', ...] */ -export function harmonics (list) { - var a = asArr(list) - return a.length ? compact(a.map(interval(a[0]))) : a +export function harmonics(list) { + var a = asArr(list); + return a.length ? compact(a.map(interval(a[0]))) : a; } /** @@ -56,13 +56,13 @@ export function harmonics (list) { * harmonizer.intervallic('e g c') // => ['3m', '4P'] * harmonizer.intervallic('c') // => [] */ -export function intervallic (notes) { - var dist = [] - notes = asArr(notes) +export function intervallic(notes) { + var dist = []; + notes = asArr(notes); for (var i = 1; i < notes.length; i++) { - dist.push(interval(notes[i - 1], notes[i])) + dist.push(interval(notes[i - 1], notes[i])); } - return dist + return dist; } /** @@ -87,9 +87,9 @@ export function intervallic (notes) { * var C = tonal.harmonizer('C D E') * C('M3') // => ['E', 'G#', 'B'] */ -export function harmonize (list, pitch) { - if (arguments.length > 1) return harmonize(list)(pitch) - return function (tonic) { - return compact(map(tr(tonic || 'P1'), list)) - } +export function harmonize(list, pitch) { + if (arguments.length > 1) return harmonize(list)(pitch); + return function(tonic) { + return compact(map(tr(tonic || "P1"), list)); + }; } diff --git a/packages/core/harmonizer/test/harmonizer.test.js b/packages/core/harmonizer/test/harmonizer.test.js index aa91653..b12e342 100644 --- a/packages/core/harmonizer/test/harmonizer.test.js +++ b/packages/core/harmonizer/test/harmonizer.test.js @@ -1,31 +1,31 @@ /* global describe test expect */ -var _ = require('../') +var _ = require("../"); -describe('tonal-harmonizer', () => { - test('harmonics', () => { - expect(_.harmonics('C E G')).toEqual([ '1P', '3M', '5P' ]) - expect(_.harmonics('C2 E3 G4')).toEqual([ '1P', '10M', '19P' ]) - expect(_.harmonics('x y z')).toEqual([]) - }) +describe("tonal-harmonizer", () => { + test("harmonics", () => { + expect(_.harmonics("C E G")).toEqual(["1P", "3M", "5P"]); + expect(_.harmonics("C2 E3 G4")).toEqual(["1P", "10M", "19P"]); + expect(_.harmonics("x y z")).toEqual([]); + }); - test('distances', () => { - expect(_.intervallic('c e g')).toEqual([ '3M', '3m' ]) - expect(_.intervallic('e g c')).toEqual([ '3m', '4P' ]) - expect(_.intervallic('C2 g4 c4')).toEqual([ '19P', '-5P' ]) - }) + test("distances", () => { + expect(_.intervallic("c e g")).toEqual(["3M", "3m"]); + expect(_.intervallic("e g c")).toEqual(["3m", "4P"]); + expect(_.intervallic("C2 g4 c4")).toEqual(["19P", "-5P"]); + }); - test('harmonize', () => { - expect(_.harmonize('1P 3M 5P', 'A4')).toEqual([ 'A4', 'C#5', 'E5' ]) - expect(_.harmonize('C E G', 'M3')).toEqual([ 'E', 'G#', 'B' ]) + test("harmonize", () => { + expect(_.harmonize("1P 3M 5P", "A4")).toEqual(["A4", "C#5", "E5"]); + expect(_.harmonize("C E G", "M3")).toEqual(["E", "G#", "B"]); - expect(_.harmonize('C blah D', '7m')).toEqual([ 'Bb', 'C' ]) - expect(_.harmonize(null, '7m')).toEqual([]) - expect(_.harmonize('c d e', null)).toEqual([ 'C', 'D', 'E' ]) + expect(_.harmonize("C blah D", "7m")).toEqual(["Bb", "C"]); + expect(_.harmonize(null, "7m")).toEqual([]); + expect(_.harmonize("c d e", null)).toEqual(["C", "D", "E"]); - var maj7 = _.harmonize('1P 3M 5P 7M') - expect(maj7('Bb')).toEqual([ 'Bb', 'D', 'F', 'A' ]) + var maj7 = _.harmonize("1P 3M 5P 7M"); + expect(maj7("Bb")).toEqual(["Bb", "D", "F", "A"]); - var diminished = _.harmonize('P1 m3 5d', 'C') - expect(diminished).toEqual(['C', 'Eb', 'Gb']) - }) -}) + var diminished = _.harmonize("P1 m3 5d", "C"); + expect(diminished).toEqual(["C", "Eb", "Gb"]); + }); +}); diff --git a/packages/core/midi/index.js b/packages/core/midi/index.js index 57165bb..43c347f 100644 --- a/packages/core/midi/index.js +++ b/packages/core/midi/index.js @@ -15,7 +15,7 @@ * @module midi */ -import { midi } from 'note-parser' +import { midi } from "note-parser"; /** * Convert the given note to a midi note number. If you pass a midi number it @@ -28,13 +28,14 @@ import { midi } from 'note-parser' * midi.toMidi(60) // => 60 * midi.toMidi('60') // => 60 */ -export function toMidi (val) { - if (Array.isArray(val) && val.length === 2) return val[0] * 7 + val[1] * 12 + 12 - return midi(val) +export function toMidi(val) { + if (Array.isArray(val) && val.length === 2) + return val[0] * 7 + val[1] * 12 + 12; + return midi(val); } -var FLATS = 'C Db D Eb E F Gb G Ab A Bb B'.split(' ') -var SHARPS = 'C C# D D# E F F# G G# A A# B'.split(' ') +var FLATS = "C Db D Eb E F Gb G Ab A Bb B".split(" "); +var SHARPS = "C C# D D# E F F# G G# A A# B".split(" "); /** * Given a midi number, returns a note name. The altered notes will have @@ -51,11 +52,14 @@ var SHARPS = 'C C# D D# E F F# G G# A A# B'.split(' ') * // it rounds to nearest note * midi.note(61.7) // => 'D4' */ -export function note (num, sharps) { - if (num === true || num === false) return function (m) { return note(m, num) } - 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 +export function note(num, sharps) { + if (num === true || num === false) + return function(m) { + return note(m, num); + }; + 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; } diff --git a/packages/core/midi/test/midi.test.js b/packages/core/midi/test/midi.test.js index 5b3c9ee..cb0303d 100644 --- a/packages/core/midi/test/midi.test.js +++ b/packages/core/midi/test/midi.test.js @@ -1,46 +1,91 @@ /* global describe test expect */ -var midi = require('..') -var map = function (fn, s) { - return (Array.isArray(s) ? s : s.split(' ')).map(fn) -} +var midi = require(".."); +var map = function(fn, s) { + return (Array.isArray(s) ? s : s.split(" ")).map(fn); +}; -describe('tonal-midi', () => { - test('toMidi - map note names to note numbers', () => { - expect(map(midi.toMidi, 'C4 D4 E4 F4 G4 A4 B4 C5')).toEqual([ 60, 62, 64, 65, 67, 69, 71, 72 ]) - expect(map(midi.toMidi, 'C4 B#3 Dbb4')).toEqual([60, 60, 60]) - }) - test('toMidi - pitch classes do not have midi', () => { - expect(map(midi.toMidi, 'C D E F G A B')).toEqual([ null, null, null, null, null, null, null ]) - }) - test('toMidi - midi numbers are bypassed', () => { - expect(midi.toMidi(72)).toBe(72) - expect(midi.toMidi('60') === 60).toBeTruthy() - }) - test('toMidi - invalid values', () => { - expect(midi.toMidi(null)).toBe(null) - expect(midi.toMidi(-1)).toBe(null) - expect(midi.toMidi(128)).toBe(null) - }) - test('toMidi - accepts pitch in array notation', () => { - expect(midi.toMidi([0, 4])).toBe(60) - expect(midi.toMidi([3, 3])).toBe(69) +describe("tonal-midi", () => { + test("toMidi - map note names to note numbers", () => { + expect(map(midi.toMidi, "C4 D4 E4 F4 G4 A4 B4 C5")).toEqual([ + 60, + 62, + 64, + 65, + 67, + 69, + 71, + 72 + ]); + expect(map(midi.toMidi, "C4 B#3 Dbb4")).toEqual([60, 60, 60]); + }); + test("toMidi - pitch classes do not have midi", () => { + expect(map(midi.toMidi, "C D E F G A B")).toEqual([ + null, + null, + null, + null, + null, + null, + null + ]); + }); + test("toMidi - midi numbers are bypassed", () => { + expect(midi.toMidi(72)).toBe(72); + expect(midi.toMidi("60") === 60).toBeTruthy(); + }); + test("toMidi - invalid values", () => { + expect(midi.toMidi(null)).toBe(null); + expect(midi.toMidi(-1)).toBe(null); + expect(midi.toMidi(128)).toBe(null); + }); + test("toMidi - accepts pitch in array notation", () => { + expect(midi.toMidi([0, 4])).toBe(60); + expect(midi.toMidi([3, 3])).toBe(69); // pitch classes doesn't have midi - expect(midi.toMidi([5])).toBe(null) - }) - test('note - get names from midi numbers', () => { - expect(map(midi.note, [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72])).toEqual( - [ 'C4', 'Db4', 'D4', 'Eb4', 'E4', 'F4', 'Gb4', 'G4', 'Ab4', 'A4', 'Bb4', 'B4', 'C5' ] - ) - }) - test('note with type', () => { - expect(midi.note(61, false)).toEqual('Db4') - expect(midi.note(false)(61)).toEqual('Db4') - expect(midi.note(61, true)).toEqual('C#4') - expect(midi.note(true)(61)).toEqual('C#4') - }) - test('note - partially applied with true', () => { - expect(map(midi.note(true), [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72])).toEqual( - [ 'C4', 'C#4', 'D4', 'D#4', 'E4', 'F4', 'F#4', 'G4', 'G#4', 'A4', 'A#4', 'B4', 'C5' ] - ) - }) -}) + expect(midi.toMidi([5])).toBe(null); + }); + test("note - get names from midi numbers", () => { + expect( + map(midi.note, [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72]) + ).toEqual([ + "C4", + "Db4", + "D4", + "Eb4", + "E4", + "F4", + "Gb4", + "G4", + "Ab4", + "A4", + "Bb4", + "B4", + "C5" + ]); + }); + test("note with type", () => { + expect(midi.note(61, false)).toEqual("Db4"); + expect(midi.note(false)(61)).toEqual("Db4"); + expect(midi.note(61, true)).toEqual("C#4"); + expect(midi.note(true)(61)).toEqual("C#4"); + }); + test("note - partially applied with true", () => { + expect( + map(midi.note(true), [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72]) + ).toEqual([ + "C4", + "C#4", + "D4", + "D#4", + "E4", + "F4", + "F#4", + "G4", + "G#4", + "A4", + "A#4", + "B4", + "C5" + ]); + }); +}); diff --git a/packages/core/notation/index.js b/packages/core/notation/index.js index e94fff5..3bcfe3d 100644 --- a/packages/core/notation/index.js +++ b/packages/core/notation/index.js @@ -22,9 +22,9 @@ * @param {String} letter - the letter * @return {Integer} the step number (from 0 to 6) */ -export function toStep (l) { - var s = 'CDEFGAB'.indexOf(l.toUpperCase()) - return s < 0 ? null : s +export function toStep(l) { + var s = "CDEFGAB".indexOf(l.toUpperCase()); + return s < 0 ? null : s; } /** @@ -32,15 +32,17 @@ export function toStep (l) { * @param {Integer} step - the step number * @return {Boolean} true if it's a valid step number, false otherwise */ -export function isStep (d) { return !(d < 0 || d > 6) } +export function isStep(d) { + return !(d < 0 || d > 6); +} /** * Given a step, return a letter * @param {Integer} step - the step number * @return {String} the note letter or null if not valid step number */ -export function toLetter (s) { - return isStep(s) ? 'CDEFGAB'.charAt(s) : null +export function toLetter(s) { + return isStep(s) ? "CDEFGAB".charAt(s) : null; } // ACCIDENTALS @@ -51,13 +53,17 @@ export function toLetter (s) { * @param {String} str - the string to test * @return {Boolean} true if all charaters are `b`, false otherwise */ -export function areFlats (s) { return /^b+$/.test(s) } +export function areFlats(s) { + return /^b+$/.test(s); +} /** * Test if a string are all sharps (`#`) chars * @param {String} str - the string to test * @return {Boolean} true if all charaters are `#`, false otherwise */ -export function areSharps (s) { return /^#+$/.test(s) } +export function areSharps(s) { + return /^#+$/.test(s); +} /** * Given an accidentals string return its alteration, the number @@ -68,14 +74,15 @@ export function areSharps (s) { return /^#+$/.test(s) } * toAlt('###') // => 3 * toAlt('bbb') // => -3 */ -export function toAlt (s) { - return s === '' ? 0 - : areFlats(s) ? -s.length - : areSharps(s) ? s.length - : null +export function toAlt(s) { + return s === "" + ? 0 + : areFlats(s) ? -s.length : areSharps(s) ? s.length : null; } -function fillStr (s, num) { return Array(num + 1).join(s) } +function fillStr(s, num) { + return Array(num + 1).join(s); +} /** * Given an alteration number, returns the accidentals string @@ -86,6 +93,6 @@ function fillStr (s, num) { return Array(num + 1).join(s) } * toAcc(3) // => '###' * toAcc(-3) // => 'bbb' */ -export function toAcc (n) { - return !n ? '' : n < 0 ? fillStr('b', -n) : fillStr('#', n) +export function toAcc(n) { + return !n ? "" : n < 0 ? fillStr("b", -n) : fillStr("#", n); } diff --git a/packages/core/notation/test/notation.test.js b/packages/core/notation/test/notation.test.js index d7667e4..fb01369 100644 --- a/packages/core/notation/test/notation.test.js +++ b/packages/core/notation/test/notation.test.js @@ -1,29 +1,89 @@ /* global describe test expect */ -var n = require('..') +var n = require(".."); -describe('tonal-notation', () => { - test('letter to step', () => { - expect('ABCDEFGHIJKLMNO'.split('').map(n.toStep)) - .toEqual([ 5, 6, 0, 1, 2, 3, 4, null, null, null, null, null, null, null, null ]) - expect('abcdefghijklmno'.split('').map(n.toStep)) - .toEqual([ 5, 6, 0, 1, 2, 3, 4, null, null, null, null, null, null, null, null ]) - expect('123456789'.split('').map(n.toStep)) - .toEqual([ null, null, null, null, null, null, null, null, null ]) - }) +describe("tonal-notation", () => { + test("letter to step", () => { + expect("ABCDEFGHIJKLMNO".split("").map(n.toStep)).toEqual([ + 5, + 6, + 0, + 1, + 2, + 3, + 4, + null, + null, + null, + null, + null, + null, + null, + null + ]); + expect("abcdefghijklmno".split("").map(n.toStep)).toEqual([ + 5, + 6, + 0, + 1, + 2, + 3, + 4, + null, + null, + null, + null, + null, + null, + null, + null + ]); + expect("123456789".split("").map(n.toStep)).toEqual([ + null, + null, + null, + null, + null, + null, + null, + null, + null + ]); + }); - test('step to letter', () => { - expect([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(n.toLetter)) - .toEqual([ 'C', 'D', 'E', 'F', 'G', 'A', 'B', null, null, null, null ]) - }) + test("step to letter", () => { + expect([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(n.toLetter)).toEqual([ + "C", + "D", + "E", + "F", + "G", + "A", + "B", + null, + null, + null, + null + ]); + }); - test('toAlt', () => { - expect([ 'bbbb', 'bbb', 'bb', 'b', '', '#', '##', '###', '####' ].map(n.toAlt)) - .toEqual([ -4, -3, -2, -1, 0, 1, 2, 3, 4 ]) - }) + test("toAlt", () => { + expect( + ["bbbb", "bbb", "bb", "b", "", "#", "##", "###", "####"].map(n.toAlt) + ).toEqual([-4, -3, -2, -1, 0, 1, 2, 3, 4]); + }); - test('toAcc', () => { - expect(n.toAcc()).toEqual('') - expect([-4, -3, -2, -1, 0, 1, 2, 3, 4].map(n.toAcc)) - .toEqual([ 'bbbb', 'bbb', 'bb', 'b', '', '#', '##', '###', '####' ]) - }) -}) + test("toAcc", () => { + expect(n.toAcc()).toEqual(""); + expect([-4, -3, -2, -1, 0, 1, 2, 3, 4].map(n.toAcc)).toEqual([ + "bbbb", + "bbb", + "bb", + "b", + "", + "#", + "##", + "###", + "####" + ]); + }); +}); diff --git a/packages/core/pcset/index.js b/packages/core/pcset/index.js index 324a447..607c9cf 100644 --- a/packages/core/pcset/index.js +++ b/packages/core/pcset/index.js @@ -18,13 +18,18 @@ * * @module pcset */ -import { chr, asPitch } from 'tonal-pitch' -import { pc } from 'tonal-note' -import { map, asArr, rotate, compact } from 'tonal-array' -import { transpose } from 'tonal-transpose' +import { chr, asPitch } from "tonal-pitch"; +import { pc } from "tonal-note"; +import { map, asArr, rotate, compact } from "tonal-array"; +import { transpose } from "tonal-transpose"; -function chrToInt (set) { return parseInt(chroma(set), 2) } -function pitchChr (p) { p = asPitch(p); return p ? chr(p) : null } +function chrToInt(set) { + return parseInt(chroma(set), 2); +} +function pitchChr(p) { + p = asPitch(p); + return p ? chr(p) : null; +} /** * Get chroma of a pitch class set. A chroma identifies each set uniquely. @@ -38,13 +43,13 @@ function pitchChr (p) { p = asPitch(p); return p ? chr(p) : null } * @example * pcset.chroma('C D E') // => '1010100000000' */ -export function chroma (set) { - if (isChroma(set)) return set - var b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - map(pitchChr, set).forEach(function (i) { - b[i] = 1 - }) - return b.join('') +export function chroma(set) { + if (isChroma(set)) return set; + var b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + map(pitchChr, set).forEach(function(i) { + b[i] = 1; + }); + return b.join(""); } /** @@ -55,15 +60,15 @@ export function chroma (set) { * @param {String|Array} notes - the pitch class set notes * @return {Array} an array of pitch class sets */ -export function notes (notes) { +export function notes(notes) { // FIXME: move to collection - console.warn('pcset.notes deprecated. Use collection.pcset') - var pcs = map(pc, notes) - if (!pcs.length) return pcs - var tonic = pcs[0] + console.warn("pcset.notes deprecated. Use collection.pcset"); + var pcs = map(pc, notes); + if (!pcs.length) return pcs; + var tonic = pcs[0]; // since the first note of the chroma is always C, we have to rotate it - var rotated = rotate(pitchChr(tonic), chroma(pcs).split('')).join('') - return fromChroma(rotated, tonic) + var rotated = rotate(pitchChr(tonic), chroma(pcs).split("")).join(""); + return fromChroma(rotated, tonic); } /** @@ -80,24 +85,26 @@ export function notes (notes) { * @example * pcset.modes('C E G') */ -export function modes (set, normalize) { - normalize = normalize !== false - var binary = chroma(set).split('') - return compact(binary.map(function (_, i) { - var r = rotate(i, binary) - return normalize && r[0] === '0' ? null : r.join('') - })) +export function modes(set, normalize) { + normalize = normalize !== false; + var binary = chroma(set).split(""); + return compact( + binary.map(function(_, i) { + var r = rotate(i, binary); + return normalize && r[0] === "0" ? null : r.join(""); + }) + ); } /** * @deprecated * @see modes */ -export function chromaModes (set, norm) { - console.warn('pcset.chromaModes deprecated. Renamed to pcset.modes') - return modes(set, norm) +export function chromaModes(set, norm) { + console.warn("pcset.chromaModes deprecated. Renamed to pcset.modes"); + return modes(set, norm); } -var REGEX = /^[01]{12}$/ +var REGEX = /^[01]{12}$/; /** * Test if the given string is a pitch class set chroma. @@ -107,11 +114,11 @@ var REGEX = /^[01]{12}$/ * pcset.isChroma('101010101010') // => true * pcset.isChroma('101001') // => false */ -export function isChroma (set) { - return REGEX.test(set) +export function isChroma(set) { + return REGEX.test(set); } -var IVLS = '1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M'.split(' ') +var IVLS = "1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M".split(" "); /** * Given a pcset (notes or chroma) return it's intervals * @param {String|Array} pcset - the pitch class set (notes or chroma) @@ -119,10 +126,14 @@ var IVLS = '1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M'.split(' ') * @example * pcset.intervals('1010100000000') => ['C', 'D', 'E'] */ -export function intervals (set) { - return compact(chroma(set).split('').map(function (d, i) { - return d === '1' ? IVLS[i] : null - })) +export function intervals(set) { + return compact( + chroma(set) + .split("") + .map(function(d, i) { + return d === "1" ? IVLS[i] : null; + }) + ); } /** @@ -136,11 +147,16 @@ export function intervals (set) { * @example * pcset.fromChroma('101010101010', 'C') // => ['C', 'D', 'E', 'Gb', 'Ab', 'Bb'] */ -export function fromChroma (binary, tonic) { - console.warn('pcset.fromChroma is deprecated. Use pcset.intervals().map(...)') - if (arguments.length === 1) return function (t) { return fromChroma(binary, t) } - if (!tonic) tonic = 'P1' - return intervals(binary).map(transpose(tonic)) +export function fromChroma(binary, tonic) { + console.warn( + "pcset.fromChroma is deprecated. Use pcset.intervals().map(...)" + ); + if (arguments.length === 1) + return function(t) { + return fromChroma(binary, t); + }; + if (!tonic) tonic = "P1"; + return intervals(binary).map(transpose(tonic)); } /** @@ -152,13 +168,16 @@ export function fromChroma (binary, tonic) { * @example * pcset.isEqual('c2 d3', 'c5 d2') // => true */ -export function isEqual (s1, s2) { - if (arguments.length === 1) return function (s) { return isEqual(s1, s) } - return chroma(s1) === chroma(s2) +export function isEqual(s1, s2) { + if (arguments.length === 1) + return function(s) { + return isEqual(s1, s); + }; + return chroma(s1) === chroma(s2); } -export function equal (a, b) { - console.warn('pcset.equal is deprecated. Use pcset.isEqual') - return isEqual(a, b) +export function equal(a, b) { + console.warn("pcset.equal is deprecated. Use pcset.isEqual"); + return isEqual(a, b); } /** @@ -170,14 +189,17 @@ export function equal (a, b) { * @example * pcset.subset('c d e', 'C2 D4 D5 C6') // => true */ -export function isSubset (set, test) { - if (arguments.length === 1) return function (t) { return isSubset(set, t) } - test = chrToInt(test) - return (test & chrToInt(set)) === test +export function isSubset(set, test) { + if (arguments.length === 1) + return function(t) { + return isSubset(set, t); + }; + test = chrToInt(test); + return (test & chrToInt(set)) === test; } -export function subset (a, b) { - console.warn('pcset.subset is deprecated. Use pcset.isSubset') - return isSubset(a, b) +export function subset(a, b) { + console.warn("pcset.subset is deprecated. Use pcset.isSubset"); + return isSubset(a, b); } /** @@ -189,14 +211,17 @@ export function subset (a, b) { * @example * pcset.isSuperset('c d e', 'C2 D4 F4 D5 E5 C6') // => true */ -export function isSuperset (set, test) { - if (arguments.length === 1) return function (t) { return isSuperset(set, t) } - test = chrToInt(test) - return (test | chrToInt(set)) === test +export function isSuperset(set, test) { + if (arguments.length === 1) + return function(t) { + return isSuperset(set, t); + }; + test = chrToInt(test); + return (test | chrToInt(set)) === test; } -export function superset (a, b) { - console.warn('pcset.superset is deprecated. Use pcset.isSuperset') - return isSuperset(a, b) +export function superset(a, b) { + console.warn("pcset.superset is deprecated. Use pcset.isSuperset"); + return isSuperset(a, b); } /** @@ -208,10 +233,12 @@ export function superset (a, b) { * pcset.includes('c d e', 'C4') // =A true * pcset.includes('c d e', 'C#4') // =A false */ -export function includes (set, note) { - if (arguments.length > 1) return includes(set)(note) - set = chroma(set) - return function (note) { return set[pitchChr(note)] === '1' } +export function includes(set, note) { + if (arguments.length > 1) return includes(set)(note); + set = chroma(set); + return function(note) { + return set[pitchChr(note)] === "1"; + }; } /** @@ -225,7 +252,10 @@ export function includes (set, note) { * pcset.filter('c d e', 'c2 c#2 d2 c3 c#3 d3') // => [ 'c2', 'd2', 'c3', 'd3' ]) * pcset.filter('c2', 'c2 c#2 d2 c3 c#3 d3') // => [ 'c2', 'c3' ]) */ -export function filter (set, notes) { - if (arguments.length === 1) return function (n) { return filter(set, n) } - return asArr(notes).filter(includes(set)) +export function filter(set, notes) { + if (arguments.length === 1) + return function(n) { + return filter(set, n); + }; + return asArr(notes).filter(includes(set)); } diff --git a/packages/core/pcset/test/pcset.test.js b/packages/core/pcset/test/pcset.test.js index a3d5daf..3cf1845 100644 --- a/packages/core/pcset/test/pcset.test.js +++ b/packages/core/pcset/test/pcset.test.js @@ -1,83 +1,123 @@ /* global describe test expect */ -var pcset = require('..') +var pcset = require(".."); -describe('tonal-pcset', () => { - test('notes', () => { - expect(pcset.notes('g4 f5 g3 d3 a3 a4 c6 a1')).toEqual([ 'G', 'A', 'C', 'D', 'F' ]) - }) - test('notes always return an array', () => { - expect(pcset.notes('blah blip')).toEqual([]) - expect(pcset.notes()).toEqual([]) - }) +describe("tonal-pcset", () => { + test("notes", () => { + expect(pcset.notes("g4 f5 g3 d3 a3 a4 c6 a1")).toEqual([ + "G", + "A", + "C", + "D", + "F" + ]); + }); + test("notes always return an array", () => { + expect(pcset.notes("blah blip")).toEqual([]); + expect(pcset.notes()).toEqual([]); + }); - test('chroma', () => { - expect(pcset.chroma('c d e')).toBe('101010000000') - expect(pcset.chroma('g g#4 a bb5')).toBe('000000011110') - expect(pcset.chroma('P1 M2 M3 P4 P5 M6 M7')).toBe(pcset.chroma('c d e f g a b')) - expect(pcset.chroma('101010101010')).toBe('101010101010') - }) + test("chroma", () => { + expect(pcset.chroma("c d e")).toBe("101010000000"); + expect(pcset.chroma("g g#4 a bb5")).toBe("000000011110"); + expect(pcset.chroma("P1 M2 M3 P4 P5 M6 M7")).toBe( + pcset.chroma("c d e f g a b") + ); + expect(pcset.chroma("101010101010")).toBe("101010101010"); + }); - test('intervals', () => { - expect(pcset.intervals('101010101010')).toEqual([ '1P', '2M', '3M', '5d', '6m', '7m' ]) - expect(pcset.intervals('1010')).toEqual([]) - }) + test("intervals", () => { + expect(pcset.intervals("101010101010")).toEqual([ + "1P", + "2M", + "3M", + "5d", + "6m", + "7m" + ]); + expect(pcset.intervals("1010")).toEqual([]); + }); - test('modes', () => { + test("modes", () => { // TODO: fixme, the 4th mode should have F# instead of Gb - expect(pcset.modes('c d e f g a b').map(function (chroma, i) { - return pcset.fromChroma(chroma, 'C') - })).toEqual([ [ 'C', 'D', 'E', 'F', 'G', 'A', 'B' ], - [ 'C', 'D', 'Eb', 'F', 'G', 'A', 'Bb' ], - [ 'C', 'Db', 'Eb', 'F', 'G', 'Ab', 'Bb' ], - [ 'C', 'D', 'E', 'Gb', 'G', 'A', 'B' ], - [ 'C', 'D', 'E', 'F', 'G', 'A', 'Bb' ], - [ 'C', 'D', 'Eb', 'F', 'G', 'Ab', 'Bb' ], - [ 'C', 'Db', 'Eb', 'F', 'Gb', 'Ab', 'Bb' ] ]) - }) + expect( + pcset.modes("c d e f g a b").map(function(chroma, i) { + return pcset.fromChroma(chroma, "C"); + }) + ).toEqual([ + ["C", "D", "E", "F", "G", "A", "B"], + ["C", "D", "Eb", "F", "G", "A", "Bb"], + ["C", "Db", "Eb", "F", "G", "Ab", "Bb"], + ["C", "D", "E", "Gb", "G", "A", "B"], + ["C", "D", "E", "F", "G", "A", "Bb"], + ["C", "D", "Eb", "F", "G", "Ab", "Bb"], + ["C", "Db", "Eb", "F", "Gb", "Ab", "Bb"] + ]); + }); - test('isChroma', () => { - expect(pcset.isChroma('101010101010')).toBe(true) - expect(pcset.isChroma('1010101')).toBe(false) - expect(pcset.isChroma('blah')).toBe(false) - expect(pcset.isChroma('c d e')).toBe(false) - }) + test("isChroma", () => { + expect(pcset.isChroma("101010101010")).toBe(true); + expect(pcset.isChroma("1010101")).toBe(false); + expect(pcset.isChroma("blah")).toBe(false); + expect(pcset.isChroma("c d e")).toBe(false); + }); - test('subset', () => { - expect(pcset.isSubset('c4 d5 e6', 'c2 d3')).toBe(true) - expect(pcset.isSubset('c4 d5 e6', 'c2 d3 e5')).toBe(true) - expect(pcset.isSubset('c d e', 'c d e f')).toBe(false) - expect(pcset.isSubset('c d e')('c2 d3 f6')).toBe(false) - }) + test("subset", () => { + expect(pcset.isSubset("c4 d5 e6", "c2 d3")).toBe(true); + expect(pcset.isSubset("c4 d5 e6", "c2 d3 e5")).toBe(true); + expect(pcset.isSubset("c d e", "c d e f")).toBe(false); + expect(pcset.isSubset("c d e")("c2 d3 f6")).toBe(false); + }); - test('superset', () => { - expect(pcset.isSuperset('c d e', 'c2 d3 e4 f5')).toBe(true) - expect(pcset.isSuperset('c d e', 'e f g')).toBe(false) - expect(pcset.isSuperset('c d e')('d e')).toBe(false) - }) + test("superset", () => { + expect(pcset.isSuperset("c d e", "c2 d3 e4 f5")).toBe(true); + expect(pcset.isSuperset("c d e", "e f g")).toBe(false); + expect(pcset.isSuperset("c d e")("d e")).toBe(false); + }); - test('equal', () => { - expect(pcset.isEqual('c2 d3 e7 f5', 'c4 c d5 e6 f1')).toBeTruthy() - expect(pcset.isEqual('c f')('c4 c f1')).toBeTruthy() - }) + test("equal", () => { + expect(pcset.isEqual("c2 d3 e7 f5", "c4 c d5 e6 f1")).toBeTruthy(); + expect(pcset.isEqual("c f")("c4 c f1")).toBeTruthy(); + }); - test('includes', () => { - expect(pcset.includes('c d e', 'C4')).toBe(true) - expect(pcset.includes('c d e', 'C#4')).toBe(false) - }) + test("includes", () => { + expect(pcset.includes("c d e", "C4")).toBe(true); + expect(pcset.includes("c d e", "C#4")).toBe(false); + }); - test('filter', () => { - expect(pcset.filter('c d e', 'c2 c#2 d2 c3 c#3 d3')).toEqual([ 'c2', 'd2', 'c3', 'd3' ]) - expect(pcset.filter('c')('c2 c#2 d2 c3 c#3 d3')).toEqual([ 'c2', 'c3' ]) - }) + test("filter", () => { + expect(pcset.filter("c d e", "c2 c#2 d2 c3 c#3 d3")).toEqual([ + "c2", + "d2", + "c3", + "d3" + ]); + expect(pcset.filter("c")("c2 c#2 d2 c3 c#3 d3")).toEqual(["c2", "c3"]); + }); - test('modes', () => { - expect(pcset.modes('c d e f g a b')).toEqual([ - '101011010101', '101101010110', '110101011010', '101010110101', - '101011010110', '101101011010', '110101101010' ]) - expect(pcset.modes('c d e f g a b', false)).toEqual([ - '101011010101', '010110101011', '101101010110', '011010101101', - '110101011010', '101010110101', '010101101011', '101011010110', - '010110101101', '101101011010', '011010110101', '110101101010' ]) - expect(pcset.modes('blah bleh')).toEqual([]) - }) -}) + test("modes", () => { + expect(pcset.modes("c d e f g a b")).toEqual([ + "101011010101", + "101101010110", + "110101011010", + "101010110101", + "101011010110", + "101101011010", + "110101101010" + ]); + expect(pcset.modes("c d e f g a b", false)).toEqual([ + "101011010101", + "010110101011", + "101101010110", + "011010101101", + "110101011010", + "101010110101", + "010101101011", + "101011010110", + "010110101101", + "101101011010", + "011010110101", + "110101101010" + ]); + expect(pcset.modes("blah bleh")).toEqual([]); + }); +}); diff --git a/packages/core/pitch/index.js b/packages/core/pitch/index.js index 2f8bc87..e93bac3 100644 --- a/packages/core/pitch/index.js +++ b/packages/core/pitch/index.js @@ -8,9 +8,9 @@ * @private * @module pitch */ -import { parse as noteParse, build as noteStr } from 'note-parser' -import { parse as ivlParse, altToQ } from 'interval-notation' -import { encode as enc, decode as dec } from 'tonal-encoding' +import { parse as noteParse, build as noteStr } from "note-parser"; +import { parse as ivlParse, altToQ } from "interval-notation"; +import { encode as enc, decode as dec } from "tonal-encoding"; /** * Create a pitch @@ -19,15 +19,17 @@ import { encode as enc, decode as dec } from 'tonal-encoding' * @param {Integer} dir - (Optional) Only required for intervals. Can be 1 or -1 * @return {Pitch} */ -export function pitch (fifths, focts, dir) { - return dir ? ['tnlp', [fifths, focts], dir] : ['tnlp', [fifths, focts]] +export function pitch(fifths, focts, dir) { + return dir ? ["tnlp", [fifths, focts], dir] : ["tnlp", [fifths, focts]]; } /** * Test if an object is a pitch * @param {Pitch} * @return {Boolean} */ -export function isPitch (p) { return Array.isArray(p) && p[0] === 'tnlp' } +export function isPitch(p) { + return Array.isArray(p) && p[0] === "tnlp"; +} /** * Encode a pitch * @param {Integer} step @@ -35,8 +37,8 @@ export function isPitch (p) { return Array.isArray(p) && p[0] === 'tnlp' } * @param {Integer} oct * @param {Integer} dir - (Optional) */ -export function encode (s, a, o, dir) { - return dir ? ['tnlp', enc(s, a, o), dir] : ['tnlp', enc(s, a, o)] +export function encode(s, a, o, dir) { + return dir ? ["tnlp", enc(s, a, o), dir] : ["tnlp", enc(s, a, o)]; } /** @@ -44,8 +46,8 @@ export function encode (s, a, o, dir) { * @param {Pitch} the pitch * @return {Array} An array with [step, alt, oct] */ -export function decode (p) { - return dec.apply(null, p[1]) +export function decode(p) { + return dec.apply(null, p[1]); } /** @@ -53,53 +55,67 @@ export function decode (p) { * @param {Pitch} * @return {String} 'ivl' or 'note' or null if not a pitch */ -export function pType (p) { - return !isPitch(p) ? null : p[2] ? 'ivl' : 'note' +export function pType(p) { + return !isPitch(p) ? null : p[2] ? "ivl" : "note"; } /** * Test if is a pitch note (with or without octave) * @param {Pitch} * @return {Boolean} */ -export function isNotePitch (p) { return pType(p) === 'note' } +export function isNotePitch(p) { + return pType(p) === "note"; +} /** * Test if is an interval * @param {Pitch} * @return {Boolean} */ -export function isIvlPitch (p) { return pType(p) === 'ivl' } +export function isIvlPitch(p) { + return pType(p) === "ivl"; +} /** * Test if is a pitch class (a pitch note without octave) * @param {Pitch} * @return {Boolean} */ -export function isPC (p) { return isPitch(p) && p[1].length === 1 } +export function isPC(p) { + return isPitch(p) && p[1].length === 1; +} /** * Get direction of a pitch (even for notes) * @param {Pitch} * @return {Integer} 1 or -1 */ -export function dir (p) { return p[2] === -1 ? -1 : 1 } +export function dir(p) { + return p[2] === -1 ? -1 : 1; +} /** * Get encoded fifths from pitch. * @param {Pitch} * @return {Integer} */ -export function fifths (p) { return p[2] === -1 ? -p[1][0] : p[1][0] } +export function fifths(p) { + return p[2] === -1 ? -p[1][0] : p[1][0]; +} /** * Get encoded octaves from pitch. * @param {Pitch} * @return {Integer} */ -export function focts (p) { return p[2] === -1 ? -p[1][1] : p[1][1] } +export function focts(p) { + return p[2] === -1 ? -p[1][1] : p[1][1]; +} /** * Get height of a pitch. * @param {Pitch} * @return {Integer} */ -export function height (p) { return fifths(p) * 7 + focts(p) * 12 } +export function height(p) { + return fifths(p) * 7 + focts(p) * 12; +} /** * Get chroma of a pitch. The chroma is a number between 0 and 11 to represent @@ -109,18 +125,18 @@ export function height (p) { return fifths(p) * 7 + focts(p) * 12 } * @param {Pitch} * @return {Integer} */ -export function chr (p) { - var f = fifths(p) - return 7 * f - 12 * Math.floor(f * 7 / 12) +export function chr(p) { + var f = fifths(p); + return 7 * f - 12 * Math.floor(f * 7 / 12); } // memoize parsers -function memoize (fn) { - var cache = {} - return function (str) { - if (typeof str !== 'string') return null - return cache[str] || (cache[str] = fn(str)) - } +function memoize(fn) { + var cache = {}; + return function(str) { + if (typeof str !== "string") return null; + return cache[str] || (cache[str] = fn(str)); + }; } /** @@ -129,10 +145,10 @@ function memoize (fn) { * @param {String} str * @return {Pitch} the pitch or null if not valid note string */ -export var parseNote = memoize(function (s) { - var p = noteParse(s) - return p ? encode(p.step, p.alt, p.oct) : null -}) +export var parseNote = memoize(function(s) { + var p = noteParse(s); + return p ? encode(p.step, p.alt, p.oct) : null; +}); /** * Parse an interval @@ -140,18 +156,20 @@ export var parseNote = memoize(function (s) { * @param {String} str * @return {Pitch} the pitch or null if not valid interval string */ -export var parseIvl = memoize(function (s) { - var p = ivlParse(s) - if (!p) return null - return p ? encode(p.simple - 1, p.alt, p.oct, p.dir) : null -}) +export var parseIvl = memoize(function(s) { + var p = ivlParse(s); + if (!p) return null; + return p ? encode(p.simple - 1, p.alt, p.oct, p.dir) : null; +}); /** * Parse a note or an interval * @param {String} str * @return {Pitch} the pitch or null if not valid pitch string */ -export function parsePitch (s) { return parseNote(s) || parseIvl(s) } +export function parsePitch(s) { + return parseNote(s) || parseIvl(s); +} /** * Ensure the given object is a note pitch. If is a string, it will be @@ -159,30 +177,36 @@ export function parsePitch (s) { return parseNote(s) || parseIvl(s) } * @param {Pitch|String} * @return {Pitch} */ -export function asNotePitch (p) { return isNotePitch(p) ? p : parseNote(p) } +export function asNotePitch(p) { + return isNotePitch(p) ? p : parseNote(p); +} /** * Ensure the given object is a interval pitch. If is a string, it will be * parsed. If not a interval pitch or valid interval string, it returns null. * @param {Pitch|String} * @return {Pitch} */ -export function asIvlPitch (p) { return isIvlPitch(p) ? p : parseIvl(p) } +export function asIvlPitch(p) { + return isIvlPitch(p) ? p : parseIvl(p); +} /** * Ensure the given object is a pitch. If is a string, it will be * parsed. If not a pitch or valid pitch string, it returns null. * @param {Pitch|String} * @return {Pitch} */ -export function asPitch (p) { return isPitch(p) ? p : parsePitch(p) } +export function asPitch(p) { + return isPitch(p) ? p : parsePitch(p); +} /** * Convert a note pitch to string representation * @param {Pitch} * @return {String} */ -export function strNote (p) { - if (!isNotePitch(p)) return null - return noteStr.apply(null, decode(p)) +export function strNote(p) { + if (!isNotePitch(p)) return null; + return noteStr.apply(null, decode(p)); } /** @@ -190,13 +214,13 @@ export function strNote (p) { * @param {Pitch} * @return {String} */ -export function strIvl (p) { - if (!isIvlPitch(p)) return null +export function strIvl(p) { + if (!isIvlPitch(p)) return null; // decode to [step, alt, oct] - var d = decode(p) + var d = decode(p); // d = [step, alt, oct] - var num = d[0] + 1 + 7 * d[2] - return p[2] * num + altToQ(num, d[1]) + var num = d[0] + 1 + 7 * d[2]; + return p[2] * num + altToQ(num, d[1]); } /** @@ -204,23 +228,25 @@ export function strIvl (p) { * @param {Pitch} * @return {String} */ -export function strPitch (p) { return strNote(p) || strIvl(p) } +export function strPitch(p) { + return strNote(p) || strIvl(p); +} // A function that creates a decorator // The returned function can _decorate_ other functions to parse and build // string representations -function decorator (is, parse, str) { - return function (fn) { - return function (v) { - var i = is(v) +function decorator(is, parse, str) { + return function(fn) { + return function(v) { + var i = is(v); // if the value is in pitch notation no conversion - if (i) return fn(v) + if (i) return fn(v); // else parse the pitch - var p = parse(v) + var p = parse(v); // if parsed, apply function and back to string - return p ? str(fn(p)) : null - } - } + return p ? str(fn(p)) : null; + }; + }; } /** @@ -231,7 +257,7 @@ function decorator (is, parse, str) { * @param {Function} fn * @return {Function} the decorated function */ -export var noteFn = decorator(isNotePitch, parseNote, strNote) +export var noteFn = decorator(isNotePitch, parseNote, strNote); /** * Decorate a function to work internally with interval pitches, even if the * parameters are provided as strings. Also it converts back the result @@ -240,7 +266,7 @@ export var noteFn = decorator(isNotePitch, parseNote, strNote) * @param {Function} fn * @return {Function} the decorated function */ -export var ivlFn = decorator(isIvlPitch, parseIvl, strIvl) +export var ivlFn = decorator(isIvlPitch, parseIvl, strIvl); /** * Decorate a function to work internally with pitches, even if the * parameters are provided as strings. Also it converts back the result @@ -249,4 +275,4 @@ export var ivlFn = decorator(isIvlPitch, parseIvl, strIvl) * @param {Function} fn * @return {Function} the decorated function */ -export var pitchFn = decorator(isPitch, parsePitch, strPitch) +export var pitchFn = decorator(isPitch, parsePitch, strPitch); diff --git a/packages/core/pitch/test/pitch.test.js b/packages/core/pitch/test/pitch.test.js index 9cf617f..7828126 100644 --- a/packages/core/pitch/test/pitch.test.js +++ b/packages/core/pitch/test/pitch.test.js @@ -1,71 +1,129 @@ /* global describe test expect */ -var p = require('..') +var p = require(".."); -describe('tonal-pitch', () => { - test('pitch', () => { - expect(p.pitch(2, 2)).toEqual(['tnlp', [2, 2]]) - expect(p.pitch(2, 2, -1)).toEqual(['tnlp', [2, 2], -1]) - }) - test('get fifths from pitch object', () => { - expect(p.fifths(p.parseIvl('2M'))).toBe(2) - expect(p.fifths(p.parseIvl('-2M'))).toBe(-2) - }) +describe("tonal-pitch", () => { + test("pitch", () => { + expect(p.pitch(2, 2)).toEqual(["tnlp", [2, 2]]); + expect(p.pitch(2, 2, -1)).toEqual(["tnlp", [2, 2], -1]); + }); + test("get fifths from pitch object", () => { + expect(p.fifths(p.parseIvl("2M"))).toBe(2); + expect(p.fifths(p.parseIvl("-2M"))).toBe(-2); + }); - test('get focts from pitch object', () => { - expect(p.focts(p.parseIvl('2M'))).toBe(-1) - expect(p.focts(p.parseIvl('-2M'))).toBe(1) - }) + test("get focts from pitch object", () => { + expect(p.focts(p.parseIvl("2M"))).toBe(-1); + expect(p.focts(p.parseIvl("-2M"))).toBe(1); + }); - test('get dir from pitch object', () => { - expect(p.dir(p.parseIvl('2M'))).toBe(1) - expect(p.dir(p.parseIvl('-2M'))).toBe(-1) - expect(p.dir(p.parseNote('C4'))).toBe(1) - expect(p.dir(p.parseNote('C-1'))).toBe(1) - }) + test("get dir from pitch object", () => { + expect(p.dir(p.parseIvl("2M"))).toBe(1); + expect(p.dir(p.parseIvl("-2M"))).toBe(-1); + expect(p.dir(p.parseNote("C4"))).toBe(1); + expect(p.dir(p.parseNote("C-1"))).toBe(1); + }); - test('get chroma from a pitch object', () => { - expect('Cb C Db D Eb E Fb F Gb G Ab A Bb B'.split(' ').map(p.parseNote).map(p.chr)) - .toEqual([ 11, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11 ]) - expect('P1 M2 M3 P4 P5 M6 M7'.split(' ').map(p.parseIvl).map(p.chr)) - .toEqual([ 0, 2, 4, 5, 7, 9, 11 ]) - expect('-1P -2M -3M -4P -5P -6M -7M'.split(' ').map(p.parseIvl).map(p.chr)) - .toEqual([ 0, 10, 8, 7, 5, 3, 1 ]) - }) + test("get chroma from a pitch object", () => { + expect( + "Cb C Db D Eb E Fb F Gb G Ab A Bb B" + .split(" ") + .map(p.parseNote) + .map(p.chr) + ).toEqual([11, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11]); + expect( + "P1 M2 M3 P4 P5 M6 M7" + .split(" ") + .map(p.parseIvl) + .map(p.chr) + ).toEqual([0, 2, 4, 5, 7, 9, 11]); + expect( + "-1P -2M -3M -4P -5P -6M -7M" + .split(" ") + .map(p.parseIvl) + .map(p.chr) + ).toEqual([0, 10, 8, 7, 5, 3, 1]); + }); - test('parse note', () => { - expect(p.parseNote('Cb4')).toEqual([ 'tnlp', [ -7, 8 ] ]) - }) + test("parse note", () => { + expect(p.parseNote("Cb4")).toEqual(["tnlp", [-7, 8]]); + }); - test('parse interval', () => { - expect(p.parseIvl('10m')).toEqual([ 'tnlp', [ -3, 3 ], 1 ]) - expect(p.parseIvl('m10')).toEqual([ 'tnlp', [ -3, 3 ], 1 ]) - expect(p.parseIvl('3M')).toEqual([ 'tnlp', [ 4, -2 ], 1 ]) - expect(p.parseIvl('-3M')).toEqual([ 'tnlp', [ 4, -2 ], -1 ]) - }) + test("parse interval", () => { + expect(p.parseIvl("10m")).toEqual(["tnlp", [-3, 3], 1]); + expect(p.parseIvl("m10")).toEqual(["tnlp", [-3, 3], 1]); + expect(p.parseIvl("3M")).toEqual(["tnlp", [4, -2], 1]); + expect(p.parseIvl("-3M")).toEqual(["tnlp", [4, -2], -1]); + }); - test('parse pitch', () => { - expect(p.parsePitch('Cb4')).toEqual(p.parseNote('Cb4')) - expect(p.parsePitch('A11')).toEqual(p.parseNote('A11')) - expect(p.parsePitch('11A')).toEqual(p.parseIvl('A11')) - }) + test("parse pitch", () => { + expect(p.parsePitch("Cb4")).toEqual(p.parseNote("Cb4")); + expect(p.parsePitch("A11")).toEqual(p.parseNote("A11")); + expect(p.parsePitch("11A")).toEqual(p.parseIvl("A11")); + }); - test('note to string', () => { - function id (n) { return p.strNote(p.parseNote(n)) } - expect('a c db2 e#4 fx6 gbbb ab#9'.split(' ').map(id)) - .toEqual([ 'A', 'C', 'Db2', 'E#4', 'F##6', 'Gbbb', null ]) - }) + test("note to string", () => { + function id(n) { + return p.strNote(p.parseNote(n)); + } + expect("a c db2 e#4 fx6 gbbb ab#9".split(" ").map(id)).toEqual([ + "A", + "C", + "Db2", + "E#4", + "F##6", + "Gbbb", + null + ]); + }); - test('interval to string', () => { - function id (i) { return p.strIvl(p.parseIvl(i)) } - expect('1P 2M 3M 4P 5P 6M 7M'.split(' ').map(id)) - .toEqual([ '1P', '2M', '3M', '4P', '5P', '6M', '7M' ]) - expect('1d 2m 3m 4d 5d 6m 7m'.split(' ').map(id)) - .toEqual([ '1d', '2m', '3m', '4d', '5d', '6m', '7m' ]) - expect('8A 9A 10A 11A 12A 13A 14A'.split(' ').map(id)) - .toEqual([ '8A', '9A', '10A', '11A', '12A', '13A', '14A' ]) - expect('-1P -2M -3M -4P -5P -6M -7M'.split(' ').map(id)) - .toEqual([ '-1P', '-2M', '-3M', '-4P', '-5P', '-6M', '-7M' ]) - expect('-8d -9m -10m -11d -12d -13m -14m'.split(' ').map(id)) - .toEqual([ '-8d', '-9m', '-10m', '-11d', '-12d', '-13m', '-14m' ]) - }) -}) + test("interval to string", () => { + function id(i) { + return p.strIvl(p.parseIvl(i)); + } + expect("1P 2M 3M 4P 5P 6M 7M".split(" ").map(id)).toEqual([ + "1P", + "2M", + "3M", + "4P", + "5P", + "6M", + "7M" + ]); + expect("1d 2m 3m 4d 5d 6m 7m".split(" ").map(id)).toEqual([ + "1d", + "2m", + "3m", + "4d", + "5d", + "6m", + "7m" + ]); + expect("8A 9A 10A 11A 12A 13A 14A".split(" ").map(id)).toEqual([ + "8A", + "9A", + "10A", + "11A", + "12A", + "13A", + "14A" + ]); + expect("-1P -2M -3M -4P -5P -6M -7M".split(" ").map(id)).toEqual([ + "-1P", + "-2M", + "-3M", + "-4P", + "-5P", + "-6M", + "-7M" + ]); + expect("-8d -9m -10m -11d -12d -13m -14m".split(" ").map(id)).toEqual([ + "-8d", + "-9m", + "-10m", + "-11d", + "-12d", + "-13m", + "-14m" + ]); + }); +}); diff --git a/packages/core/range/index.js b/packages/core/range/index.js index 4c23fc3..0c58547 100644 --- a/packages/core/range/index.js +++ b/packages/core/range/index.js @@ -18,22 +18,33 @@ g * @module range */ -import { asArr, map } from 'tonal-array' -import { trFifths } from 'tonal-transpose' -import { toMidi, note } from 'tonal-midi' -import { filter } from 'tonal-pcset' +import { asArr, map } from "tonal-array"; +import { trFifths } from "tonal-transpose"; +import { toMidi, note } from "tonal-midi"; +import { filter } from "tonal-pcset"; -function isNum (n) { return typeof n === 'number' } +function isNum(n) { + return typeof n === "number"; +} // convert notes to midi if needed -function asNum (n) { return isNum(n) ? n : toMidi(n) } +function asNum(n) { + return isNum(n) ? n : toMidi(n); +} // ascending range -function ascR (b, n) { for (var a = []; n--; a[n] = n + b); return a } +function ascR(b, n) { + for (var a = []; n--; a[n] = n + b); + return a; +} // descending range -function descR (b, n) { for (var a = []; n--; a[n] = b - n); return a } +function descR(b, n) { + for (var a = []; n--; a[n] = b - n); + return a; +} // create a range between a and b -function ran (a, b) { - return a === null || b === null ? [] - : a < b ? ascR(a, b - a + 1) : descR(a, a - b + 1) +function ran(a, b) { + return a === null || b === null + ? [] + : a < b ? ascR(a, b - a + 1) : descR(a, a - b + 1); } /** @@ -52,12 +63,14 @@ function ran (a, b) { * // can be expressed with a string or array * range.numeric('C2 C4 C2') === range.numeric(['C2', 'C4', 'C2']) */ -export function numeric (list) { - return asArr(list).map(asNum).reduce(function (r, n, i) { - if (i === 1) return ran(r, n) - var last = r[r.length - 1] - return r.concat(ran(last, n).slice(1)) - }) +export function numeric(list) { + return asArr(list) + .map(asNum) + .reduce(function(r, n, i) { + if (i === 1) return ran(r, n); + var last = r[r.length - 1]; + return r.concat(ran(last, n).slice(1)); + }); } /** @@ -71,8 +84,8 @@ export function numeric (list) { * // with sharps * tonal.chromatic('C2 C3', true) // => [ 'C2', 'C#2', 'D2', 'D#2', 'E2', 'F2', 'F#2', 'G2', 'G#2', 'A2', 'A#2', 'B2', 'C3' ] */ -export function chromatic (list, sharps) { - return map(note(sharps === true), numeric(list)) +export function chromatic(list, sharps) { + return map(note(sharps === true), numeric(list)); } /** @@ -84,8 +97,8 @@ export function chromatic (list, sharps) { * @example * range.fifths('C', [0, 6]) // => [ 'C', 'G', 'D', 'A', 'E', 'B', 'F#' ]) */ -export function fifths (tonic, range) { - return numeric(range).map(trFifths(tonic)) +export function fifths(tonic, range) { + return numeric(range).map(trFifths(tonic)); } /** @@ -101,8 +114,11 @@ export function fifths (tonic, range) { * range.pitchSet('C D E F G A B', ['C3', 'C2']) * // => [ 'C3', 'B2', 'A2', 'G2', 'F2', 'E2', 'D2', 'C2' ] */ -export function pitchSet (set, range) { - if (arguments.length === 1) return function (l) { return pitchSet(set, l) } +export function pitchSet(set, range) { + if (arguments.length === 1) + return function(l) { + return pitchSet(set, l); + }; - return filter(set, chromatic(range)) + return filter(set, chromatic(range)); } diff --git a/packages/core/range/test/range.test.js b/packages/core/range/test/range.test.js index f749bc1..2c96548 100644 --- a/packages/core/range/test/range.test.js +++ b/packages/core/range/test/range.test.js @@ -1,43 +1,169 @@ /* global describe test expect */ -var range = require('../') +var range = require("../"); -describe('tonal-range', () => { - test('numeric - multiple notes in a string', () => { - expect(range.numeric('C2 F2 Bb1 C2')).toEqual([ 36, 37, 38, 39, 40, 41, 40, 39, 38, 37, 36, 35, 34, 35, 36 ]) - }) +describe("tonal-range", () => { + test("numeric - multiple notes in a string", () => { + expect(range.numeric("C2 F2 Bb1 C2")).toEqual([ + 36, + 37, + 38, + 39, + 40, + 41, + 40, + 39, + 38, + 37, + 36, + 35, + 34, + 35, + 36 + ]); + }); - test('fifths', () => { - expect(range.fifths('C', [0, 6])).toEqual([ 'C', 'G', 'D', 'A', 'E', 'B', 'F#' ]) - expect(range.fifths('C', [0, -6])).toEqual([ 'C', 'F', 'Bb', 'Eb', 'Ab', 'Db', 'Gb' ]) - }) + test("fifths", () => { + expect(range.fifths("C", [0, 6])).toEqual([ + "C", + "G", + "D", + "A", + "E", + "B", + "F#" + ]); + expect(range.fifths("C", [0, -6])).toEqual([ + "C", + "F", + "Bb", + "Eb", + "Ab", + "Db", + "Gb" + ]); + }); - test('numeric - numbers array', () => { - expect(range.numeric([0, 10])).toEqual([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]) - expect(range.numeric([10, 0])).toEqual([ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ]) - expect(range.numeric([0, -5])).toEqual([ 0, -1, -2, -3, -4, -5 ]) - expect(range.numeric([-5, -10])).toEqual([ -5, -6, -7, -8, -9, -10 ]) - }) - test('numeric - notes array', () => { - expect(range.numeric('C4 C5')).toEqual([ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72 ]) - expect(range.numeric(['C5', 'C4'])).toEqual([ 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60 ]) - }) - test('chromatic', () => { - expect(range.chromatic('A3 A4')).toEqual( - [ 'A3', 'Bb3', 'B3', 'C4', 'Db4', 'D4', 'Eb4', 'E4', 'F4', 'Gb4', 'G4', 'Ab4', 'A4' ] - ) - expect(range.chromatic('A4 A3')).toEqual( - [ 'A4', 'Ab4', 'G4', 'Gb4', 'F4', 'E4', 'Eb4', 'D4', 'Db4', 'C4', 'B3', 'Bb3', 'A3' ] - ) - expect(range.chromatic('C3 Eb3 A2')).toEqual([ 'C3', 'Db3', 'D3', 'Eb3', 'D3', 'Db3', 'C3', 'B2', 'Bb2', 'A2' ]) - expect(range.chromatic('C3 Eb3 A2')).toEqual(range.chromatic(['C3', 'Eb3', 'A2'])) - }) - test('chromatic - sharps', () => { - expect(range.chromatic('C2 C3', true)).toEqual( - [ 'C2', 'C#2', 'D2', 'D#2', 'E2', 'F2', 'F#2', 'G2', 'G#2', 'A2', 'A#2', 'B2', 'C3' ] - ) - }) - test('pitchSet', () => { - expect(range.pitchSet('C D E', 'C2 C4')).toEqual([ 'C2', 'D2', 'E2', 'C3', 'D3', 'E3', 'C4' ]) - expect(range.pitchSet('C D E F G A B', 'C3 C2')).toEqual([ 'C3', 'B2', 'A2', 'G2', 'F2', 'E2', 'D2', 'C2' ]) - }) -}) + test("numeric - numbers array", () => { + expect(range.numeric([0, 10])).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + expect(range.numeric([10, 0])).toEqual([10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]); + expect(range.numeric([0, -5])).toEqual([0, -1, -2, -3, -4, -5]); + expect(range.numeric([-5, -10])).toEqual([-5, -6, -7, -8, -9, -10]); + }); + test("numeric - notes array", () => { + expect(range.numeric("C4 C5")).toEqual([ + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72 + ]); + expect(range.numeric(["C5", "C4"])).toEqual([ + 72, + 71, + 70, + 69, + 68, + 67, + 66, + 65, + 64, + 63, + 62, + 61, + 60 + ]); + }); + test("chromatic", () => { + expect(range.chromatic("A3 A4")).toEqual([ + "A3", + "Bb3", + "B3", + "C4", + "Db4", + "D4", + "Eb4", + "E4", + "F4", + "Gb4", + "G4", + "Ab4", + "A4" + ]); + expect(range.chromatic("A4 A3")).toEqual([ + "A4", + "Ab4", + "G4", + "Gb4", + "F4", + "E4", + "Eb4", + "D4", + "Db4", + "C4", + "B3", + "Bb3", + "A3" + ]); + expect(range.chromatic("C3 Eb3 A2")).toEqual([ + "C3", + "Db3", + "D3", + "Eb3", + "D3", + "Db3", + "C3", + "B2", + "Bb2", + "A2" + ]); + expect(range.chromatic("C3 Eb3 A2")).toEqual( + range.chromatic(["C3", "Eb3", "A2"]) + ); + }); + test("chromatic - sharps", () => { + expect(range.chromatic("C2 C3", true)).toEqual([ + "C2", + "C#2", + "D2", + "D#2", + "E2", + "F2", + "F#2", + "G2", + "G#2", + "A2", + "A#2", + "B2", + "C3" + ]); + }); + test("pitchSet", () => { + expect(range.pitchSet("C D E", "C2 C4")).toEqual([ + "C2", + "D2", + "E2", + "C3", + "D3", + "E3", + "C4" + ]); + expect(range.pitchSet("C D E F G A B", "C3 C2")).toEqual([ + "C3", + "B2", + "A2", + "G2", + "F2", + "E2", + "D2", + "C2" + ]); + }); +}); diff --git a/packages/core/tonal/README.md b/packages/core/tonal/README.md index 6d55ddf..a61b9ac 100644 --- a/packages/core/tonal/README.md +++ b/packages/core/tonal/README.md @@ -1,6 +1,6 @@ # tonal [![npm](https://img.shields.io/npm/v/tonal.svg?style=flat-square)](https://www.npmjs.com/package/tonal) -[![Build Status](https://travis-ci.org/danigb/tonal.svg?branch=master&style=flat-square)](https://travis-ci.org/danigb/tonal) [![Code Climate](https://codeclimate.com/github/danigb/tonal/badges/gpa.svg?style=flat-square)](https://codeclimate.com/github/danigb/tonal) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard) [![license](https://img.shields.io/npm/l/tonal.svg?style=flat-square)](https://www.npmjs.com/package/tonal) +[![Build Status](https://travis-ci.org/danigb/tonal.svg?branch=master&style=flat-square)](https://travis-ci.org/danigb/tonal) [![Code Climate](https://codeclimate.com/github/danigb/tonal/badges/gpa.svg?style=flat-square)](https://codeclimate.com/github/danigb/tonal) [![license](https://img.shields.io/npm/l/tonal.svg?style=flat-square)](https://www.npmjs.com/package/tonal) [![codecov](https://codecov.io/gh/danigb/tonal/branch/master/graph/badge.svg)](https://codecov.io/gh/danigb/tonal) diff --git a/packages/core/tonal/index.js b/packages/core/tonal/index.js index 2008d6d..dffffa1 100644 --- a/packages/core/tonal/index.js +++ b/packages/core/tonal/index.js @@ -21,44 +21,48 @@ * * @module tonal */ -import * as array from 'tonal-array' -import * as transpose from 'tonal-transpose' -import * as harmonizer from 'tonal-harmonizer' -import * as distance from 'tonal-distance' -import * as note from 'tonal-note' -import * as interval from 'tonal-interval' -import * as midi from 'tonal-midi' -import * as freq from 'tonal-freq' -import * as range from 'tonal-range' -import * as key from 'tonal-key' -import * as scale from 'tonal-scale' -import * as chord from 'tonal-chord' -import * as pitch from 'tonal-pitch' -import * as notation from 'tonal-notation' -import * as progression from 'tonal-progression' -import * as sonority from 'tonal-sonority' -import * as pitchset from 'tonal-pitchset' -import * as pcset from 'tonal-pcset' +import * as array from "tonal-array"; +import * as transpose from "tonal-transpose"; +import * as harmonizer from "tonal-harmonizer"; +import * as distance from "tonal-distance"; +import * as note from "tonal-note"; +import * as interval from "tonal-interval"; +import * as midi from "tonal-midi"; +import * as freq from "tonal-freq"; +import * as range from "tonal-range"; +import * as key from "tonal-key"; +import * as scale from "tonal-scale"; +import * as chord from "tonal-chord"; +import * as pitch from "tonal-pitch"; +import * as notation from "tonal-notation"; +import * as progression from "tonal-progression"; +import * as sonority from "tonal-sonority"; +import * as pitchset from "tonal-pitchset"; +import * as pcset from "tonal-pcset"; -var assign = Object.assign -var tonal = assign({}, array, transpose, harmonizer, distance) -tonal.pitch = pitch -tonal.notation = notation -tonal.note = note -tonal.ivl = interval -tonal.midi = midi -tonal.freq = freq -tonal.range = range -tonal.key = key -tonal.progression = progression -tonal.sonority = sonority -tonal.pitchset = pitchset -tonal.pcset = pcset +var assign = Object.assign; +var tonal = assign({}, array, transpose, harmonizer, distance); +tonal.pitch = pitch; +tonal.notation = notation; +tonal.note = note; +tonal.ivl = interval; +tonal.midi = midi; +tonal.freq = freq; +tonal.range = range; +tonal.key = key; +tonal.progression = progression; +tonal.sonority = sonority; +tonal.pitchset = pitchset; +tonal.pcset = pcset; -tonal.scale = function (name) { return tonal.scale.notes(name) } -assign(tonal.scale, scale) -tonal.chord = function (name) { return tonal.chord.notes(name) } -assign(tonal.chord, chord) +tonal.scale = function(name) { + return tonal.scale.notes(name); +}; +assign(tonal.scale, scale); +tonal.chord = function(name) { + return tonal.chord.notes(name); +}; +assign(tonal.chord, chord); -if (typeof window !== 'undefined') window.Tonal = tonal -export default tonal +if (typeof window !== "undefined") window.Tonal = tonal; +export default tonal; diff --git a/packages/core/tonal/test/tonal.test.js b/packages/core/tonal/test/tonal.test.js index b6e9e3d..d3ecbf2 100644 --- a/packages/core/tonal/test/tonal.test.js +++ b/packages/core/tonal/test/tonal.test.js @@ -1,19 +1,19 @@ /* global describe test expect */ -var tonal = require('..') +var tonal = require(".."); -describe('tonal', () => { - test('exports', () => { - expect(Object.keys(tonal).length).toBe(31) - Object.keys(tonal).forEach(function (name) { - expect(tonal[name]).not.toBe(undefined) - }) - }) - test('functions', () => { - expect(typeof tonal.scale).toBe('function') - expect(typeof tonal.chord).toBe('function') - }) - test('aliases', () => { - expect(tonal.scale('C major')).toEqual(tonal.scale.notes('C major')) - expect(tonal.chord('Cmaj7')).toEqual(tonal.chord.notes('Cmaj7')) - }) -}) +describe("tonal", () => { + test("exports", () => { + expect(Object.keys(tonal).length).toBe(31); + Object.keys(tonal).forEach(function(name) { + expect(tonal[name]).not.toBe(undefined); + }); + }); + test("functions", () => { + expect(typeof tonal.scale).toBe("function"); + expect(typeof tonal.chord).toBe("function"); + }); + test("aliases", () => { + expect(tonal.scale("C major")).toEqual(tonal.scale.notes("C major")); + expect(tonal.chord("Cmaj7")).toEqual(tonal.chord.notes("Cmaj7")); + }); +}); diff --git a/packages/core/transpose/index.js b/packages/core/transpose/index.js index d07174f..aee06ef 100644 --- a/packages/core/transpose/index.js +++ b/packages/core/transpose/index.js @@ -11,18 +11,27 @@ * * @module transpose */ -import { pitch, pType, fifths, focts, height, isPC, - asPitch, isIvlPitch, strPitch } from 'tonal-pitch' +import { + pitch, + pType, + fifths, + focts, + height, + isPC, + asPitch, + isIvlPitch, + strPitch +} from "tonal-pitch"; -function trBy (i, p) { - var t = pType(p) - if (!t) return null - var f = fifths(i) + fifths(p) - if (isPC(p)) return ['tnlp', [f]] - var o = focts(i) + focts(p) - if (t === 'note') return ['tnlp', [f, o]] - var d = height(i) + height(p) < 0 ? -1 : 1 - return ['tnlp', [d * f, d * o], d] +function trBy(i, p) { + var t = pType(p); + if (!t) return null; + var f = fifths(i) + fifths(p); + if (isPC(p)) return ["tnlp", [f]]; + var o = focts(i) + focts(p); + if (t === "note") return ["tnlp", [f, o]]; + var d = height(i) + height(p) < 0 ? -1 : 1; + return ["tnlp", [d * f, d * o], d]; } /** @@ -45,13 +54,15 @@ function trBy (i, p) { * // can be partially applied * _.map(_.transpose('3M'), 'c d e f g') // => ['E', 'F#', 'G#', 'A', 'B'] */ -export function transpose (a, b) { - if (arguments.length === 1) return function (b) { return transpose(a, b) } - var pa = asPitch(a) - var pb = asPitch(b) - var r = isIvlPitch(pa) ? trBy(pa, pb) - : isIvlPitch(pb) ? trBy(pb, pa) : null - return a === pa && b === pb ? r : strPitch(r) +export function transpose(a, b) { + if (arguments.length === 1) + return function(b) { + return transpose(a, b); + }; + var pa = asPitch(a); + var pb = asPitch(b); + var r = isIvlPitch(pa) ? trBy(pa, pb) : isIvlPitch(pb) ? trBy(pb, pa) : null; + return a === pa && b === pb ? r : strPitch(r); } /** @@ -67,9 +78,9 @@ export function transpose (a, b) { * // or using tonal * tonal.trFifths('G4', 1) // => 'D5' */ -export function trFifths (t, n) { - if (arguments.length > 1) return trFifths(t)(n) - return function (n) { - return transpose(t, pitch(n, 0, 1)) - } +export function trFifths(t, n) { + if (arguments.length > 1) return trFifths(t)(n); + return function(n) { + return transpose(t, pitch(n, 0, 1)); + }; } diff --git a/packages/core/transpose/test/transpose.test.js b/packages/core/transpose/test/transpose.test.js index 11052f0..4ab5b0a 100644 --- a/packages/core/transpose/test/transpose.test.js +++ b/packages/core/transpose/test/transpose.test.js @@ -1,62 +1,92 @@ /* global describe test expect */ -var t = require('../') -var tr = t.transpose +var t = require("../"); +var tr = t.transpose; -function map (fn, s) { - if (arguments.length === 1) return function (s) { return map(fn, s) } - return (Array.isArray(s) ? s : s.split(' ')).map(fn) +function map(fn, s) { + if (arguments.length === 1) + return function(s) { + return map(fn, s); + }; + return (Array.isArray(s) ? s : s.split(" ")).map(fn); } -describe('tonal-scale', () => { - test('order of params is not relevant', () => { - expect(tr('c#2', 'm3')).toEqual(tr('m3', 'c#2')) - }) - test('notes by intervals', () => { - expect(map(tr('3M'), 'c2 d3 f4 g5')) - .toEqual([ 'E2', 'F#3', 'A4', 'B5' ]) - }) - test('pitch classes by intervals', () => { - expect(map(tr('Bb'), 'P1 M3 P5 M7')) - .toEqual([ 'Bb', 'D', 'F', 'A' ]) - }) - test('transpose nulls', () => { - expect(tr('M3', 'blah')).toBe(null) - expect(tr('C2', 'blah')).toBe(null) - expect(tr(null, null)).toBe(null) - }) - test('notes by descending intervals', () => { - expect(map(tr('-2M'), 'c2 d3 f4 g5')) - .toEqual([ 'Bb1', 'C3', 'Eb4', 'F5' ]) - }) - test('intervals by intervals', () => { - expect(map(tr('3M'), '1P 2M 3M 4P 5P')) - .toEqual([ '3M', '4A', '5A', '6M', '7M' ]) - }) - test('descending intervals', () => { - expect(map(tr('-2M'), '1P 2M 3M 4P 5P')) - .toEqual([ '-2M', '1P', '2M', '3m', '4P' ]) - }) - test('all desending intervals', () => { - expect(map(tr('-2M'), '-5P -4P -3M -2M 1P')) - .toEqual(['-6M', '-5P', '-4A', '-3M', '-2M']) - }) - test('returns array notation if both params are in array notation', () => { - expect(tr(['tnlp', [1, 0], 1], ['tnlp', [1, 0]])) - .toEqual([ 'tnlp', [2, 0] ]) - }) - test('transpose edge cases', () => { - var trC = function (i) { return i.split(' ').map(tr('C2')) } - expect(trC('1d 1P 1A')).toEqual(['Cb2', 'C2', 'C#2']) - expect(trC('-1d -1P -1A')).toEqual(['C#2', 'C2', 'Cb2']) - expect(trC('2d 2m 2M 2A')).toEqual([ 'Dbb2', 'Db2', 'D2', 'D#2' ]) - expect(trC('-2d -2m -2M -2A')).toEqual([ 'B#1', 'B1', 'Bb1', 'Bbb1' ]) - expect(trC('4dd 4d 4P 4A 4AA')).toEqual([ 'Fbb2', 'Fb2', 'F2', 'F#2', 'F##2' ]) - expect(trC('5P -5P 5A -5A')).toEqual(['G2', 'F1', 'G#2', 'Fb1']) - expect(trC('6M -6M 6m -6m')).toEqual(['A2', 'Eb1', 'Ab2', 'E1']) - }) +describe("tonal-scale", () => { + test("order of params is not relevant", () => { + expect(tr("c#2", "m3")).toEqual(tr("m3", "c#2")); + }); + test("notes by intervals", () => { + expect(map(tr("3M"), "c2 d3 f4 g5")).toEqual(["E2", "F#3", "A4", "B5"]); + }); + test("pitch classes by intervals", () => { + expect(map(tr("Bb"), "P1 M3 P5 M7")).toEqual(["Bb", "D", "F", "A"]); + }); + test("transpose nulls", () => { + expect(tr("M3", "blah")).toBe(null); + expect(tr("C2", "blah")).toBe(null); + expect(tr(null, null)).toBe(null); + }); + test("notes by descending intervals", () => { + expect(map(tr("-2M"), "c2 d3 f4 g5")).toEqual(["Bb1", "C3", "Eb4", "F5"]); + }); + test("intervals by intervals", () => { + expect(map(tr("3M"), "1P 2M 3M 4P 5P")).toEqual([ + "3M", + "4A", + "5A", + "6M", + "7M" + ]); + }); + test("descending intervals", () => { + expect(map(tr("-2M"), "1P 2M 3M 4P 5P")).toEqual([ + "-2M", + "1P", + "2M", + "3m", + "4P" + ]); + }); + test("all desending intervals", () => { + expect(map(tr("-2M"), "-5P -4P -3M -2M 1P")).toEqual([ + "-6M", + "-5P", + "-4A", + "-3M", + "-2M" + ]); + }); + test("returns array notation if both params are in array notation", () => { + expect(tr(["tnlp", [1, 0], 1], ["tnlp", [1, 0]])).toEqual(["tnlp", [2, 0]]); + }); + test("transpose edge cases", () => { + var trC = function(i) { + return i.split(" ").map(tr("C2")); + }; + expect(trC("1d 1P 1A")).toEqual(["Cb2", "C2", "C#2"]); + expect(trC("-1d -1P -1A")).toEqual(["C#2", "C2", "Cb2"]); + expect(trC("2d 2m 2M 2A")).toEqual(["Dbb2", "Db2", "D2", "D#2"]); + expect(trC("-2d -2m -2M -2A")).toEqual(["B#1", "B1", "Bb1", "Bbb1"]); + expect(trC("4dd 4d 4P 4A 4AA")).toEqual([ + "Fbb2", + "Fb2", + "F2", + "F#2", + "F##2" + ]); + expect(trC("5P -5P 5A -5A")).toEqual(["G2", "F1", "G#2", "Fb1"]); + expect(trC("6M -6M 6m -6m")).toEqual(["A2", "Eb1", "Ab2", "E1"]); + }); - test('transpose fifths', () => { - expect([0, 1, 2, 3, 4, 5, 6, 7].map(t.trFifths('C'))) - .toEqual([ 'C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#' ]) - }) -}) + test("transpose fifths", () => { + expect([0, 1, 2, 3, 4, 5, 6, 7].map(t.trFifths("C"))).toEqual([ + "C", + "G", + "D", + "A", + "E", + "B", + "F#", + "C#" + ]); + }); +}); diff --git a/packages/deprecated/chords/index.js b/packages/deprecated/chords/index.js index fce26f8..d87c67e 100644 --- a/packages/deprecated/chords/index.js +++ b/packages/deprecated/chords/index.js @@ -1 +1,2 @@ -module.exports = 'This module has been renamed: https://www.npmjs.com/package/tonal-chord' +module.exports = + "This module has been renamed: https://www.npmjs.com/package/tonal-chord"; diff --git a/packages/deprecated/filter/index.js b/packages/deprecated/filter/index.js index ca0b377..599d0c2 100644 --- a/packages/deprecated/filter/index.js +++ b/packages/deprecated/filter/index.js @@ -2,4 +2,4 @@ * @private * @module filter */ -export default 'tonal-filter is deprecated. See tonal: https://github.com/danigb/tonal' +export default "tonal-filter is deprecated. See tonal: https://github.com/danigb/tonal"; diff --git a/packages/deprecated/intervals/index.js b/packages/deprecated/intervals/index.js index 8fac22a..57812f1 100644 --- a/packages/deprecated/intervals/index.js +++ b/packages/deprecated/intervals/index.js @@ -1 +1,2 @@ -module.exports = 'This module has been renamed: https://www.npmjs.com/package/tonal-interval' +module.exports = + "This module has been renamed: https://www.npmjs.com/package/tonal-interval"; diff --git a/packages/deprecated/keys/index.js b/packages/deprecated/keys/index.js index 224ef94..a58c039 100644 --- a/packages/deprecated/keys/index.js +++ b/packages/deprecated/keys/index.js @@ -1 +1,2 @@ -module.exports = 'This module has been renamed: https://www.npmjs.com/package/tonal-key' +module.exports = + "This module has been renamed: https://www.npmjs.com/package/tonal-key"; diff --git a/packages/deprecated/pitches/index.js b/packages/deprecated/pitches/index.js index ca4bcd3..12a506e 100644 --- a/packages/deprecated/pitches/index.js +++ b/packages/deprecated/pitches/index.js @@ -1 +1,2 @@ -module.exports = 'This module has been renamed: https://www.npmjs.com/package/tonal-pitch' +module.exports = + "This module has been renamed: https://www.npmjs.com/package/tonal-pitch"; diff --git a/packages/deprecated/progressions/index.js b/packages/deprecated/progressions/index.js index 3ca33fd..c42d186 100644 --- a/packages/deprecated/progressions/index.js +++ b/packages/deprecated/progressions/index.js @@ -1 +1,2 @@ -module.exports = 'This module has been renamed: https://www.npmjs.com/package/tonal-progression' +module.exports = + "This module has been renamed: https://www.npmjs.com/package/tonal-progression"; diff --git a/packages/deprecated/scales/index.js b/packages/deprecated/scales/index.js index c506c67..2243c78 100644 --- a/packages/deprecated/scales/index.js +++ b/packages/deprecated/scales/index.js @@ -1 +1,2 @@ -module.exports = 'This module has been renamed: https://www.npmjs.com/package/tonal-scale' +module.exports = + "This module has been renamed: https://www.npmjs.com/package/tonal-scale"; diff --git a/packages/extensions/key/index.js b/packages/extensions/key/index.js index adcd91f..7b8976f 100644 --- a/packages/extensions/key/index.js +++ b/packages/extensions/key/index.js @@ -13,33 +13,48 @@ * @module key */ -import { areFlats, areSharps, toAcc } from 'tonal-notation' -import { trFifths } from 'tonal-transpose' -import { pc, pcFifths } from 'tonal-note' -import { numeric } from 'tonal-range' -import { rotate } from 'tonal-array' -import { harmonics, harmonize } from 'tonal-harmonizer' +import { areFlats, areSharps, toAcc } from "tonal-notation"; +import { trFifths } from "tonal-transpose"; +import { pc, pcFifths } from "tonal-note"; +import { numeric } from "tonal-range"; +import { rotate } from "tonal-array"; +import { harmonics, harmonize } from "tonal-harmonizer"; // Order matters: use an array -var MODES = ['ionian', 'dorian', 'phrygian', 'lydian', 'mixolydian', - 'aeolian', 'locrian', 'major', 'minor'] +var MODES = [ + "ionian", + "dorian", + "phrygian", + "lydian", + "mixolydian", + "aeolian", + "locrian", + "major", + "minor" +]; // { C: 0, D: 2, E: 4, F: -1, G: 1, A: 3, B: 5 } -var FIFTHS = [0, 2, 4, -1, 1, 3, 5, 0, 3] -var SCALES = [0, 1, 2, 3, 4, 5, 6, 0, 5].map(function (n) { - return harmonics(rotate(n, ['C', 'D', 'E', 'F', 'G', 'A', 'B'])) -}) +var FIFTHS = [0, 2, 4, -1, 1, 3, 5, 0, 3]; +var SCALES = [0, 1, 2, 3, 4, 5, 6, 0, 5].map(function(n) { + return harmonics(rotate(n, ["C", "D", "E", "F", "G", "A", "B"])); +}); // PRIVATE // Given a tonic, mode pair, return the key string -function toKey (t, m) { return !t ? m : t + ' ' + m } +function toKey(t, m) { + return !t ? m : t + " " + m; +} // Given the alterations, return the major key -function majorKey (n) { return toKey(trFifths('C', n), 'major') } +function majorKey(n) { + return toKey(trFifths("C", n), "major"); +} // given the mode name, return the alterations -function modeNum (mode) { return FIFTHS[MODES.indexOf(mode)] } +function modeNum(mode) { + return FIFTHS[MODES.indexOf(mode)]; +} // given a string, return the valid mode it represents or null -function validMode (m) { - m = m.trim().toLowerCase() - return MODES.indexOf(m) === -1 ? null : m +function validMode(m) { + m = m.trim().toLowerCase(); + return MODES.indexOf(m) === -1 ? null : m; } /** @@ -54,18 +69,19 @@ function validMode (m) { * key.props('Ab bebop') // => null * key.props('blah') // => null */ -export function props (str) { - if (typeof str !== 'string') return null - var ndx = str.indexOf(' ') - var key +export function props(str) { + if (typeof str !== "string") return null; + var ndx = str.indexOf(" "); + var key; if (ndx === -1) { - var p = pc(str) - key = p ? { tonic: p, mode: 'major' } - : { tonic: false, mode: validMode(str) } + var p = pc(str); + key = p + ? { tonic: p, mode: "major" } + : { tonic: false, mode: validMode(str) }; } else { - key = { tonic: pc(str.slice(0, ndx)), mode: validMode(str.slice(ndx + 1)) } + key = { tonic: pc(str.slice(0, ndx)), mode: validMode(str.slice(ndx + 1)) }; } - return key.mode ? key : null + return key.mode ? key : null; } /** @@ -78,8 +94,8 @@ export function props (str) { * key.isKeyName('major') // => true * key.isKeyName('Bb bebop') // => false */ -export function isKeyName (name) { - return props(name) !== null +export function isKeyName(name) { + return props(name) !== null; } /** @@ -92,8 +108,8 @@ export function isKeyName (name) { * key.tonic('minor') // => false * key.tonic('bebop') // null */ -export function tonic (key) { - return (props(key) || key || {}).tonic || null +export function tonic(key) { + return (props(key) || key || {}).tonic || null; } /** @@ -106,8 +122,8 @@ export function tonic (key) { * key.mode('DORIAN') // => 'dorian' * key.mode('mixophrygian') // => null */ -export function mode (key) { - return (props(key) || key || {}).mode || null +export function mode(key) { + return (props(key) || key || {}).mode || null; } /** @@ -125,14 +141,17 @@ export function mode (key) { * minor('C major') // => 'A minor' * minor('E major') // => 'C# minor' */ -export function relative (rel, key) { - if (arguments.length === 1) return function (k) { return relative(rel, k) } - rel = props(rel) - if (!rel || rel.tonic) return null - key = props(key) - if (!key || !key.tonic) return null - var tonic = trFifths(key.tonic, modeNum(rel.mode) - modeNum(key.mode)) - return toKey(tonic, rel.mode) +export function relative(rel, key) { + if (arguments.length === 1) + return function(k) { + return relative(rel, k); + }; + rel = props(rel); + if (!rel || rel.tonic) return null; + key = props(key); + if (!key || !key.tonic) return null; + var tonic = trFifths(key.tonic, modeNum(rel.mode) - modeNum(key.mode)); + return toKey(tonic, rel.mode); } /** @@ -144,11 +163,13 @@ export function relative (rel, key) { * var key = require('tonal-keys') * key.alteredNotes('Eb major') // => [ 'Bb', 'Eb', 'Ab' ] */ -export function alteredNotes (key) { - var alt = alteration(key) - return alt === null ? null - : alt < 0 ? numeric([-1, alt]).map(trFifths('F')) - : numeric([1, alt]).map(trFifths('B')) +export function alteredNotes(key) { + var alt = alteration(key); + return alt === null + ? null + : alt < 0 + ? numeric([-1, alt]).map(trFifths("F")) + : numeric([1, alt]).map(trFifths("B")); } /** @@ -163,8 +184,8 @@ export function alteredNotes (key) { * key.modes(true) // => [ 'ionian', 'dorian', 'phrygian', 'lydian', * // 'mixolydian', 'aeolian', 'locrian', 'major', 'minor' ] */ -export function modes (alias) { - return alias ? MODES.slice() : MODES.slice(0, -2) +export function modes(alias) { + return alias ? MODES.slice() : MODES.slice(0, -2); } /** @@ -176,8 +197,8 @@ export function modes (alias) { * var key = require('tonal-key') * key.fromAlter(2) // => 'D major' */ -export function fromAlter (n) { - return typeof n === 'number' ? majorKey(n) : null +export function fromAlter(n) { + return typeof n === "number" ? majorKey(n) : null; } /** @@ -190,10 +211,10 @@ export function fromAlter (n) { * key.fromAcc('b') // => 'F major' * key.fromAcc('##') // => 'D major' */ -export function fromAcc (s) { - return areSharps(s) ? majorKey(s.length) - : areFlats(s) ? majorKey(-s.length) - : null +export function fromAcc(s) { + return areSharps(s) + ? majorKey(s.length) + : areFlats(s) ? majorKey(-s.length) : null; } /** @@ -207,10 +228,10 @@ export function fromAcc (s) { * key.scale('C dorian') // => [ 'C', 'D', 'Eb', 'F', 'G', 'A', 'Bb' ] * key.scale('E mixolydian') // => [ 'E', 'F#', 'G#', 'A', 'B', 'C#', 'D' ] */ -export function scale (key) { - var p = props(key) - if (!p || !p.tonic) return null - return harmonize(SCALES[MODES.indexOf(p.mode)], p.tonic) +export function scale(key) { + var p = props(key); + if (!p || !p.tonic) return null; + return harmonize(SCALES[MODES.indexOf(p.mode)], p.tonic); } /** @@ -222,12 +243,12 @@ export function scale (key) { * var key = require('tonal-keys') * key.alteration('A major') // => 3 */ -export function alteration (key) { - var k = props(key) - if (!k || !k.tonic) return null - var toMajor = modeNum(k.mode) - var toC = pcFifths(k.tonic) - return toC - toMajor +export function alteration(key) { + var k = props(key); + if (!k || !k.tonic) return null; + var toMajor = modeNum(k.mode); + var toC = pcFifths(k.tonic); + return toC - toMajor; } /** @@ -236,12 +257,12 @@ export function alteration (key) { * var key = require('tonal-keys') * key.signature('A major') // => '###' */ -export function signature (key) { - return toAcc(alteration(key)) +export function signature(key) { + return toAcc(alteration(key)); } /** * An alias for `signature()` * @function */ -export var accidentals = signature +export var accidentals = signature; diff --git a/packages/extensions/key/test/key.test.js b/packages/extensions/key/test/key.test.js index 2ee1f9c..ad0bfc6 100644 --- a/packages/extensions/key/test/key.test.js +++ b/packages/extensions/key/test/key.test.js @@ -1,85 +1,134 @@ /* global describe test expect */ -var key = require('..') +var key = require(".."); -describe('tonal-key', () => { - test('mode', () => { - expect(key.mode('mixophrygian')).toBe(null) - expect(key.mode('blah')).toBe(null) - expect(key.mode(null)).toBe(null) - key.modes(true).forEach(function (m) { - expect(key.mode(m)).toBe(m) - }) - }) +describe("tonal-key", () => { + test("mode", () => { + expect(key.mode("mixophrygian")).toBe(null); + expect(key.mode("blah")).toBe(null); + expect(key.mode(null)).toBe(null); + key.modes(true).forEach(function(m) { + expect(key.mode(m)).toBe(m); + }); + }); - test('tonic', () => { - expect(key.tonic('c4 mixolydian')).toBe('C') - expect(key.tonic('mixolydian')).toBe(null) - }) + test("tonic", () => { + expect(key.tonic("c4 mixolydian")).toBe("C"); + expect(key.tonic("mixolydian")).toBe(null); + }); - test('props', () => { - expect(key.props('Eb mixolydian')).toEqual({ mode: 'mixolydian', tonic: 'Eb' }) - expect(key.props('lydian')).toEqual({ mode: 'lydian', tonic: false }) - expect(key.props('F#')).toEqual({ mode: 'major', tonic: 'F#' }) - expect(key.props('blah')).toEqual(null) - expect(key.props('Eb blah')).toEqual(null) - }) + test("props", () => { + expect(key.props("Eb mixolydian")).toEqual({ + mode: "mixolydian", + tonic: "Eb" + }); + expect(key.props("lydian")).toEqual({ mode: "lydian", tonic: false }); + expect(key.props("F#")).toEqual({ mode: "major", tonic: "F#" }); + expect(key.props("blah")).toEqual(null); + expect(key.props("Eb blah")).toEqual(null); + }); - test('scale', () => { - expect(key.scale('C major')).toEqual([ 'C', 'D', 'E', 'F', 'G', 'A', 'B' ]) - expect(key.scale('C dorian')).toEqual([ 'C', 'D', 'Eb', 'F', 'G', 'A', 'Bb' ]) - expect(key.scale('E mixolydian')).toEqual([ 'E', 'F#', 'G#', 'A', 'B', 'C#', 'D' ]) - }) + test("scale", () => { + expect(key.scale("C major")).toEqual(["C", "D", "E", "F", "G", "A", "B"]); + expect(key.scale("C dorian")).toEqual([ + "C", + "D", + "Eb", + "F", + "G", + "A", + "Bb" + ]); + expect(key.scale("E mixolydian")).toEqual([ + "E", + "F#", + "G#", + "A", + "B", + "C#", + "D" + ]); + }); - test('modes', () => { - expect(key.modes(false)).toEqual( - [ 'ionian', 'dorian', 'phrygian', 'lydian', 'mixolydian', 'aeolian', 'locrian' ] - ) - expect(key.modes(true)).toEqual( - [ 'ionian', 'dorian', 'phrygian', 'lydian', 'mixolydian', 'aeolian', 'locrian', - 'major', 'minor' ] - ) - }) + test("modes", () => { + expect(key.modes(false)).toEqual([ + "ionian", + "dorian", + "phrygian", + "lydian", + "mixolydian", + "aeolian", + "locrian" + ]); + expect(key.modes(true)).toEqual([ + "ionian", + "dorian", + "phrygian", + "lydian", + "mixolydian", + "aeolian", + "locrian", + "major", + "minor" + ]); + }); - test('from alter', () => { - expect([0, 1, 2, 3, 4, 5, 6, 7].map(key.fromAlter)).toEqual([ 'C major', 'G major', 'D major', 'A major', 'E major', - 'B major', 'F# major', 'C# major' ]) - expect([-0, -1, -2, -3, -4, -5, -6, -7, -8].map(key.fromAlter)).toEqual([ 'C major', 'F major', 'Bb major', 'Eb major', 'Ab major', - 'Db major', 'Gb major', 'Cb major', 'Fb major' ]) - }) + test("from alter", () => { + expect([0, 1, 2, 3, 4, 5, 6, 7].map(key.fromAlter)).toEqual([ + "C major", + "G major", + "D major", + "A major", + "E major", + "B major", + "F# major", + "C# major" + ]); + expect([-0, -1, -2, -3, -4, -5, -6, -7, -8].map(key.fromAlter)).toEqual([ + "C major", + "F major", + "Bb major", + "Eb major", + "Ab major", + "Db major", + "Gb major", + "Cb major", + "Fb major" + ]); + }); - test('from accidentals', () => { - expect(key.fromAcc('###')).toBe('A major') - expect(key.fromAcc('bbb')).toBe('Eb major') - }) + test("from accidentals", () => { + expect(key.fromAcc("###")).toBe("A major"); + expect(key.fromAcc("bbb")).toBe("Eb major"); + }); - test('relative', () => { - expect(key.relative('minor', 'Eb major')).toBe('C minor') - expect(key.relative('dorian', 'Bb mixolydian')).toBe('F dorian') - expect(key.relative('blah', 'C major')).toBe(null) + test("relative", () => { + expect(key.relative("minor", "Eb major")).toBe("C minor"); + expect(key.relative("dorian", "Bb mixolydian")).toBe("F dorian"); + expect(key.relative("blah", "C major")).toBe(null); - var minor = key.relative('minor') - expect(minor('C')).toBe('A minor') - }) + var minor = key.relative("minor"); + expect(minor("C")).toBe("A minor"); + }); - test('alteration', () => { - expect(key.alteration('A major')).toBe(3) - var Amaj = 'A B C# D E F# G#'.split(' ') - var modes = key.modes(false) - Amaj.forEach(function (tonic, i) { - expect(key.alteration(tonic + ' ' + modes[i])).toBe(3) - }) + test("alteration", () => { + expect(key.alteration("A major")).toBe(3); + var Amaj = "A B C# D E F# G#".split(" "); + var modes = key.modes(false); + Amaj.forEach(function(tonic, i) { + expect(key.alteration(tonic + " " + modes[i])).toBe(3); + }); - expect(key.alteration('Bb major')).toBe(-2) - }) + expect(key.alteration("Bb major")).toBe(-2); + }); - test('signature', () => { - expect(key.signature('E dorian')).toBe('##') - expect(key.signature('Eb major')).toBe('bbb') - }) + test("signature", () => { + expect(key.signature("E dorian")).toBe("##"); + expect(key.signature("Eb major")).toBe("bbb"); + }); - test('alteredNotes', () => { - expect(key.alteredNotes('Eb major')).toEqual([ 'Bb', 'Eb', 'Ab' ]) - expect(key.alteredNotes('A major')).toEqual([ 'F#', 'C#', 'G#' ]) - }) -}) + test("alteredNotes", () => { + expect(key.alteredNotes("Eb major")).toEqual(["Bb", "Eb", "Ab"]); + expect(key.alteredNotes("A major")).toEqual(["F#", "C#", "G#"]); + }); +}); diff --git a/packages/extensions/progression/index.js b/packages/extensions/progression/index.js index 85781f0..362d4c7 100644 --- a/packages/extensions/progression/index.js +++ b/packages/extensions/progression/index.js @@ -8,13 +8,13 @@ * * @module progression */ -import { pc } from 'tonal-note' -import { props, fromProps } from 'tonal-interval' -import { map, compact } from 'tonal-array' -import { transpose } from 'tonal-transpose' -import { interval } from 'tonal-distance' -import { parse } from 'tonal-chord' -import { toAcc } from 'tonal-notation' +import { pc } from "tonal-note"; +import { props, fromProps } from "tonal-interval"; +import { map, compact } from "tonal-array"; +import { transpose } from "tonal-transpose"; +import { interval } from "tonal-distance"; +import { parse } from "tonal-chord"; +import { toAcc } from "tonal-notation"; /** * Given a chord progression and a tonic, return the chord progression @@ -26,25 +26,29 @@ import { toAcc } from 'tonal-notation' * @example * progression.abstract('Cmaj7 Dm7 G7', 'C') // => [ 'Imaj7', 'IIm7', 'V7' ] */ -export function abstract (chords, tonic) { - tonic = pc(tonic) - chords = map(parse, chords) - var tonics = compact(chords.map(function (x) { return x.tonic })) +export function abstract(chords, tonic) { + tonic = pc(tonic); + chords = map(parse, chords); + var tonics = compact( + chords.map(function(x) { + return x.tonic; + }) + ); // if some tonic missing, can't do the analysis - if (tonics.length !== chords.length) return null + if (tonics.length !== chords.length) return null; - return tonics.map(function (t, i) { - var p = props(interval(tonic, t)) - return buildRoman(p.num - 1, p.alt, chords[i].type) - }) + return tonics.map(function(t, i) { + var p = props(interval(tonic, t)); + return buildRoman(p.num - 1, p.alt, chords[i].type); + }); } -var NUMS = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII'] +var NUMS = ["I", "II", "III", "IV", "V", "VI", "VII"]; /** * Build an abstract chord name using roman numerals */ -export function buildRoman (num, alt, element) { - return toAcc(alt) + NUMS[num % 7] + (element || '') +export function buildRoman(num, alt, element) { + return toAcc(alt) + NUMS[num % 7] + (element || ""); } /** @@ -58,14 +62,14 @@ export function buildRoman (num, alt, element) { * var progression = require('chord-progression') * progression.concrete('I IIm7 V7', 'C') // => ['C', 'Dm7', 'G7'] */ -export function concrete (chords, tonic) { - return map(function (e) { - var r = parseRomanChord(e) - return r ? transpose(r.root, tonic) + r.type : null - }, chords) +export function concrete(chords, tonic) { + return map(function(e) { + var r = parseRomanChord(e); + return r ? transpose(r.root, tonic) + r.type : null; + }, chords); } -var ROMAN = /^\s*(b|bb|#|##|)(IV|III|II|I|VII|VI|V|iv|iii|ii|i|vii|vi|v)\s*(.*)\s*$/ +var ROMAN = /^\s*(b|bb|#|##|)(IV|III|II|I|VII|VI|V|iv|iii|ii|i|vii|vi|v)\s*(.*)\s*$/; /** * Returns a regex to match roman numbers literals with the from: * `[accidentals]roman[element]`. @@ -84,9 +88,11 @@ var ROMAN = /^\s*(b|bb|#|##|)(IV|III|II|I|VII|VI|V|iv|iii|ii|i|vii|vi|v)\s*(.*)\ * r.exec('bVImaj7') // => ['bVImaj7', 'b', 'VI', 'maj7']) * r.exec('III dom') // => ['III dom', '', 'III', 'dom']) */ -export function romanRegex () { return ROMAN } +export function romanRegex() { + return ROMAN; +} -var NUM = {i: 0, ii: 1, iii: 2, iv: 3, v: 4, vi: 5, vii: 6} +var NUM = { i: 0, ii: 1, iii: 2, iv: 3, v: 4, vi: 5, vii: 6 }; /** * Parse a chord expressed with roman numerals. It returns an interval representing @@ -103,11 +109,11 @@ var NUM = {i: 0, ii: 1, iii: 2, iv: 3, v: 4, vi: 5, vii: 6} * parse('V7') // => { root: '5P', type: '7' } * parse('bIIalt') // => { root: '2m', type: 'alt' } */ -export function parseRomanChord (str) { - var m = ROMAN.exec(str) - if (!m) return null - var num = NUM[m[2].toLowerCase()] + 1 - var alt = m[1].length - if (m[1][0] === 'b') alt = -alt - return { root: fromProps({ num: num, alt: alt, dir: 1 }), type: m[3] } +export function parseRomanChord(str) { + var m = ROMAN.exec(str); + if (!m) return null; + var num = NUM[m[2].toLowerCase()] + 1; + var alt = m[1].length; + if (m[1][0] === "b") alt = -alt; + return { root: fromProps({ num: num, alt: alt, dir: 1 }), type: m[3] }; } diff --git a/packages/extensions/progression/test/progression.test.js b/packages/extensions/progression/test/progression.test.js index c8815e9..584504a 100644 --- a/packages/extensions/progression/test/progression.test.js +++ b/packages/extensions/progression/test/progression.test.js @@ -1,49 +1,74 @@ /* global describe test expect */ -var prog = require('..') +var prog = require(".."); -describe('tonal-progression', () => { - test('concrete', () => { - expect(prog.concrete('I IIm7 V7', 'C')).toEqual(['C', 'Dm7', 'G7']) - expect(prog.concrete('Imaj7 2 IIIm7', 'C')).toEqual([ 'Cmaj7', null, 'Em7' ]) - expect(prog.concrete('I II III IV V VI VII', 'C')).toEqual([ 'C', 'D', 'E', 'F', 'G', 'A', 'B' ]) - expect(prog.concrete('bI bII bIII bIV bV bVI bVII', 'C')).toEqual([ 'Cb', 'Db', 'Eb', 'Fb', 'Gb', 'Ab', 'Bb' ]) - expect(prog.concrete('#Im7 #IIm7 #III #IVMaj7 #V7 #VI #VIIo', 'C')).toEqual([ 'C#m7', 'D#m7', 'E#', 'F#Maj7', 'G#7', 'A#', 'B#o' ]) - }) +describe("tonal-progression", () => { + test("concrete", () => { + expect(prog.concrete("I IIm7 V7", "C")).toEqual(["C", "Dm7", "G7"]); + expect(prog.concrete("Imaj7 2 IIIm7", "C")).toEqual(["Cmaj7", null, "Em7"]); + expect(prog.concrete("I II III IV V VI VII", "C")).toEqual([ + "C", + "D", + "E", + "F", + "G", + "A", + "B" + ]); + expect(prog.concrete("bI bII bIII bIV bV bVI bVII", "C")).toEqual([ + "Cb", + "Db", + "Eb", + "Fb", + "Gb", + "Ab", + "Bb" + ]); + expect( + prog.concrete("#Im7 #IIm7 #III #IVMaj7 #V7 #VI #VIIo", "C") + ).toEqual(["C#m7", "D#m7", "E#", "F#Maj7", "G#7", "A#", "B#o"]); + }); - test('abstract', () => { - expect(prog.abstract('Cmaj7 Dm7 G7', 'C')).toEqual([ 'Imaj7', 'IIm7', 'V7' ]) - }) + test("abstract", () => { + expect(prog.abstract("Cmaj7 Dm7 G7", "C")).toEqual(["Imaj7", "IIm7", "V7"]); + }); - test('progressions: build roman chord', () => { + test("progressions: build roman chord", () => { expect( - [0, 1, 2, 3, 4, 5, 6, 7, 8].map(function (n) { return prog.buildRoman(n) }) - ).toEqual([ 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'I', 'II' ]) - expect(prog.buildRoman(2, -1)).toBe('bIII') - expect(prog.buildRoman(3, 1, 'dim')).toBe('#IVdim') - }) + [0, 1, 2, 3, 4, 5, 6, 7, 8].map(function(n) { + return prog.buildRoman(n); + }) + ).toEqual(["I", "II", "III", "IV", "V", "VI", "VII", "I", "II"]); + expect(prog.buildRoman(2, -1)).toBe("bIII"); + expect(prog.buildRoman(3, 1, "dim")).toBe("#IVdim"); + }); - test('parseRomanChord', () => { - expect(prog.parseRomanChord('V7')).toEqual({ type: '7', root: '5P' }) - expect(prog.parseRomanChord('IIm7')).toEqual({ type: 'm7', root: '2M' }) - expect(prog.parseRomanChord('VIIo')).toEqual({ type: 'o', root: '7M' }) - }) + test("parseRomanChord", () => { + expect(prog.parseRomanChord("V7")).toEqual({ type: "7", root: "5P" }); + expect(prog.parseRomanChord("IIm7")).toEqual({ type: "m7", root: "2M" }); + expect(prog.parseRomanChord("VIIo")).toEqual({ type: "o", root: "7M" }); + }); - test('romanRegex', () => { - function exec (str) { return prog.romanRegex().exec(str).slice(0, 4) } - var nums = 'I II III IV V VI VII'.split(' ') - nums.forEach(function (n) { - expect(exec(n)).toEqual([n, '', n, '']) - var l = n.toLowerCase() - expect(exec(l)).toEqual([l, '', l, '']) - }) + test("romanRegex", () => { + function exec(str) { + return prog + .romanRegex() + .exec(str) + .slice(0, 4); + } + var nums = "I II III IV V VI VII".split(" "); + nums.forEach(function(n) { + expect(exec(n)).toEqual([n, "", n, ""]); + var l = n.toLowerCase(); + expect(exec(l)).toEqual([l, "", l, ""]); + }); - nums.forEach(function (n) { - '# ## b bb'.split(' ').forEach(function (alt) { - expect(exec(alt + n)).toEqual([alt + n, alt, n, '']) - }) - }) + nums.forEach(function(n) { + "# ## b bb".split(" ").forEach(function(alt) { + expect(exec(alt + n)).toEqual([alt + n, alt, n, ""]); + }); + }); - expect(exec('bVImaj7')).toEqual(['bVImaj7', 'b', 'VI', 'maj7']) - expect(exec('III dom')).toEqual(['III dom', '', 'III', 'dom']) - }) -}) + expect(exec("bVImaj7")).toEqual(["bVImaj7", "b", "VI", "maj7"]); + expect(exec("III dom")).toEqual(["III dom", "", "III", "dom"]); + }); +}); diff --git a/packages/incubator/fretboard/index.js b/packages/incubator/fretboard/index.js index f0635ad..907e83b 100644 --- a/packages/incubator/fretboard/index.js +++ b/packages/incubator/fretboard/index.js @@ -6,16 +6,18 @@ * * @module fretboard */ -import { dictionary } from 'tonal-dictionary' -import { fromSemitones } from 'tonal-interval' -import { transpose } from 'tonal-transpose' -import { pc } from 'tonal-note' -import { asArr, compact, map } from 'tonal-array' -import { numeric } from 'tonal-range' -import { includes } from 'tonal-pcset' -import DATA from './tunings.json' +import { dictionary } from "tonal-dictionary"; +import { fromSemitones } from "tonal-interval"; +import { transpose } from "tonal-transpose"; +import { pc } from "tonal-note"; +import { asArr, compact, map } from "tonal-array"; +import { numeric } from "tonal-range"; +import { includes } from "tonal-pcset"; +import DATA from "./tunings.json"; -var dict = dictionary(DATA, function (s) { return s.split(' ') }) +var dict = dictionary(DATA, function(s) { + return s.split(" "); +}); /** * Given a tuning name, returns the notes of the strings in the open position @@ -27,7 +29,7 @@ var dict = dictionary(DATA, function (s) { return s.split(' ') }) * fret.tuning('guitar') // => [ 'E2', 'A2', 'D3', 'G3', 'B3', 'E4' ] * fret.tuning('charango') // => [ 'G4', 'G4', 'C5', 'C5', 'E5', 'E4', 'A4', 'A4', 'E5', 'E5' ] */ -export var tuning = dict.get +export var tuning = dict.get; /** * Given a tuning name returns the notes of the strings in open position @@ -38,15 +40,15 @@ export var tuning = dict.get * fret.simpleTuning('guitar') => [ 'E', 'A', 'D', 'G', 'B', 'E' ] * fret.simpleTuning('charango') => [ 'G', 'C', 'E', 'A', 'E' ] */ -export function simpleTuning (src) { - var pcs = map(pc, tuning(src) || src) - var simple = pcs.reduce(function (s, pc, i) { - if (s === false) return s - else if (i % 2 === 0) s.push(pc) - else if (s[s.length - 1] !== pc) return false - return s - }, []) - return simple || pcs +export function simpleTuning(src) { + var pcs = map(pc, tuning(src) || src); + var simple = pcs.reduce(function(s, pc, i) { + if (s === false) return s; + else if (i % 2 === 0) s.push(pc); + else if (s[s.length - 1] !== pc) return false; + return s; + }, []); + return simple || pcs; } /** @@ -55,7 +57,7 @@ export function simpleTuning (src) { * @param {Boolean} aliases - get aliases or not * @return {Array} an array of tuning names */ -export var names = dict.keys +export var names = dict.keys; /** * Build a fretboard using a given tuning (or tuning name), first and last @@ -70,20 +72,26 @@ export var names = dict.keys * @param {Array|String} set - a scale or chord to filter the fretboard * @return {Array} An array of arrays, one for each string */ -export function notes (tun, first, last, set) { - first = first || 0 - last = last || first - var ivls = numeric([first, last]).map(fromSemitones) - var notes = tuning(tun) || asArr(tun) - var filterFn = set ? map(includedIn(set)) : id - return notes.map(function (b) { - return ivls.map(transpose(b)) - }).map(filterFn) +export function notes(tun, first, last, set) { + first = first || 0; + last = last || first; + var ivls = numeric([first, last]).map(fromSemitones); + var notes = tuning(tun) || asArr(tun); + var filterFn = set ? map(includedIn(set)) : id; + return notes + .map(function(b) { + return ivls.map(transpose(b)); + }) + .map(filterFn); +} +function id(o) { + return o; } -function id (o) { return o } -function includedIn (set) { - var isInSet = includes(set) - return function (n) { return isInSet(n) ? n : null } +function includedIn(set) { + var isInSet = includes(set); + return function(n) { + return isInSet(n) ? n : null; + }; } /** @@ -94,8 +102,8 @@ function includedIn (set) { * @param {Integer} last - the last fret number * @return {Array} An array of arrays, one for each string */ -export function scale (tuning, scale, first, last) { - return notes(tuning, first, last, scale) +export function scale(tuning, scale, first, last) { + return notes(tuning, first, last, scale); } /** @@ -107,38 +115,48 @@ export function scale (tuning, scale, first, last) { * @param {Integer} span - how many frets to include per position. Default 4. * @return {Array} An array of arrays, one for each possible shape. Element index is string number [ '0', '2', '2', '1', '0', '0' ] */ -export function chordShapes (tuning, notes, first, last, span) { +export function chordShapes(tuning, notes, first, last, span) { // Set defaults first = first || 0; last = last || 12; span = span || 4; - var fretboard = scale(tuning, notes, first, last) - var positions = [] + var fretboard = scale(tuning, notes, first, last); + var positions = []; // Break each string array into {fretSpan} frets overlapping sections - var strings = fretboard.map(function (string, stringIndex) { - return string.map(function (fret, fretIndex) { - return compact(string.slice(fretIndex, fretIndex + span).map(function (slicedFret, slicedFretIndex) { - // Convert note names to fret numbers - return slicedFret !== null ? fretIndex + slicedFretIndex : null - })) - }) - }) + var strings = fretboard.map(function(string, stringIndex) { + return string.map(function(fret, fretIndex) { + return compact( + string + .slice(fretIndex, fretIndex + span) + .map(function(slicedFret, slicedFretIndex) { + // Convert note names to fret numbers + return slicedFret !== null ? fretIndex + slicedFretIndex : null; + }) + ); + }); + }); // Build positions - strings.forEach(function (string) { - string.forEach(function (fretGroup, fretGroupIndex) { - if (!Array.isArray(positions[fretGroupIndex])) positions[fretGroupIndex] = [] + strings.forEach(function(string) { + string.forEach(function(fretGroup, fretGroupIndex) { + if (!Array.isArray(positions[fretGroupIndex])) + positions[fretGroupIndex] = []; - if (fretGroup.length > 1) positions[fretGroupIndex].push(fretGroup) - else positions[fretGroupIndex].push(fretGroup.toString() ? fretGroup.toString() : null) - }) - }) + if (fretGroup.length > 1) positions[fretGroupIndex].push(fretGroup); + else + positions[fretGroupIndex].push( + fretGroup.toString() ? fretGroup.toString() : null + ); + }); + }); // Remove null, neighboring duplicate arrays, and arrays with a only one non-null value - return positions.filter(function (position, i) { - if (compact(position).length < 2) return false - return i === 0 ? position : positions[i].toString() !== positions[i - 1].toString() - }) + return positions.filter(function(position, i) { + if (compact(position).length < 2) return false; + return i === 0 + ? position + : positions[i].toString() !== positions[i - 1].toString(); + }); } diff --git a/packages/incubator/fretboard/test/fretboard.test.js b/packages/incubator/fretboard/test/fretboard.test.js index 515da49..0fb791d 100644 --- a/packages/incubator/fretboard/test/fretboard.test.js +++ b/packages/incubator/fretboard/test/fretboard.test.js @@ -1,50 +1,72 @@ /* global describe test expect */ -var fr = require('..') - -describe('tonal-fretboard', () => { - test('names', () => { - expect(fr.names(true).length > fr.names(false).length).toBeTruthy() - }) - - test('tuning', () => { - expect(fr.tuning('guitar')).toEqual([ 'E2', 'A2', 'D3', 'G3', 'B3', 'E4' ]) - expect(fr.tuning('charango')).toEqual([ 'G4', 'G4', 'C5', 'C5', 'E5', 'E4', 'A4', 'A4', 'E5', 'E5' ]) - }) - - test('simple tuning', () => { - expect(fr.simpleTuning('guitar')).toEqual([ 'E', 'A', 'D', 'G', 'B', 'E' ]) - expect(fr.simpleTuning('charango')).toEqual([ 'G', 'C', 'E', 'A', 'E' ]) - }) - - test('notes', () => { - expect(fr.notes('E2 A2 D3', 0, 2)).toEqual([ [ 'E2', 'F2', 'F#2' ], [ 'A2', 'Bb2', 'B2' ], [ 'D3', 'Eb3', 'E3' ] ]) - expect(fr.notes('guitar', 5, 7)).toEqual([ [ 'A2', 'Bb2', 'B2' ], [ 'D3', 'Eb3', 'E3' ], [ 'G3', 'Ab3', 'A3' ], - [ 'C4', 'Db4', 'D4' ], [ 'E4', 'F4', 'F#4' ], [ 'A4', 'Bb4', 'B4' ] ]) - }) - - test('scale', () => { - expect(fr.scale('guitar', 'C E G', 0, 5)).toEqual( - [ [ 'E2', null, null, 'G2', null, null ], [ null, null, null, 'C3', null, null ], - [ null, null, 'E3', null, null, 'G3' ], [ 'G3', null, null, null, null, 'C4' ], - [ null, 'C4', null, null, null, 'E4' ], [ 'E4', null, null, 'G4', null, null ] ] - ) - }) - - test('chordShapes', () => { - expect(fr.chordShapes('guitar', [], 0, 5, 3)).toEqual([]) - expect(fr.chordShapes('guitar', ['G'], 0, 5, 3)).toEqual( - [ - [ '3', null, null, null, null, '3' ], - [ '3', null, '5', null, null, '3' ] - ]) - - expect(fr.chordShapes('guitar', ['E', 'G#', 'B'], 0, 5, 3)).toEqual( - [ - [ '0', '2', '2', '1', '0', '0' ], - [ null, '2', '2', '1', null, null ], - [ '4', '2', '2', '4', null, '4' ], - [ '4', null, null, '4', '5', '4' ] - ]) - }) -}) +var fr = require(".."); + +describe("tonal-fretboard", () => { + test("names", () => { + expect(fr.names(true).length > fr.names(false).length).toBeTruthy(); + }); + + test("tuning", () => { + expect(fr.tuning("guitar")).toEqual(["E2", "A2", "D3", "G3", "B3", "E4"]); + expect(fr.tuning("charango")).toEqual([ + "G4", + "G4", + "C5", + "C5", + "E5", + "E4", + "A4", + "A4", + "E5", + "E5" + ]); + }); + + test("simple tuning", () => { + expect(fr.simpleTuning("guitar")).toEqual(["E", "A", "D", "G", "B", "E"]); + expect(fr.simpleTuning("charango")).toEqual(["G", "C", "E", "A", "E"]); + }); + + test("notes", () => { + expect(fr.notes("E2 A2 D3", 0, 2)).toEqual([ + ["E2", "F2", "F#2"], + ["A2", "Bb2", "B2"], + ["D3", "Eb3", "E3"] + ]); + expect(fr.notes("guitar", 5, 7)).toEqual([ + ["A2", "Bb2", "B2"], + ["D3", "Eb3", "E3"], + ["G3", "Ab3", "A3"], + ["C4", "Db4", "D4"], + ["E4", "F4", "F#4"], + ["A4", "Bb4", "B4"] + ]); + }); + + test("scale", () => { + expect(fr.scale("guitar", "C E G", 0, 5)).toEqual([ + ["E2", null, null, "G2", null, null], + [null, null, null, "C3", null, null], + [null, null, "E3", null, null, "G3"], + ["G3", null, null, null, null, "C4"], + [null, "C4", null, null, null, "E4"], + ["E4", null, null, "G4", null, null] + ]); + }); + + test("chordShapes", () => { + expect(fr.chordShapes("guitar", [], 0, 5, 3)).toEqual([]); + expect(fr.chordShapes("guitar", ["G"], 0, 5, 3)).toEqual([ + ["3", null, null, null, null, "3"], + ["3", null, "5", null, null, "3"] + ]); + + expect(fr.chordShapes("guitar", ["E", "G#", "B"], 0, 5, 3)).toEqual([ + ["0", "2", "2", "1", "0", "0"], + [null, "2", "2", "1", null, null], + ["4", "2", "2", "4", null, "4"], + ["4", null, null, "4", "5", "4"] + ]); + }); +}); diff --git a/packages/incubator/pcset-dft/index.js b/packages/incubator/pcset-dft/index.js index 3e665ae..9f443c7 100644 --- a/packages/incubator/pcset-dft/index.js +++ b/packages/incubator/pcset-dft/index.js @@ -24,15 +24,17 @@ * @module pcset-dft */ -import { chroma } from 'tonal-note' -import { map } from 'tonal-array' -var { PI, sin, cos, pow, sqrt } = Math +import { chroma } from "tonal-note"; +import { map } from "tonal-array"; +var { PI, sin, cos, pow, sqrt } = Math; -export function pcset (notes) { - return Object.keys(map(chroma, notes).reduce(function (set, ch) { - set[ch] = true - return set - }, {})) +export function pcset(notes) { + return Object.keys( + map(chroma, notes).reduce(function(set, ch) { + set[ch] = true; + return set; + }, {}) + ); } /** @@ -46,36 +48,39 @@ export function pcset (notes) { * @example * dft.dft('C E G#') // => [ [3, 0], [0, 0], [0, 0], [3, 0], [0, 0], [0, 0], [3, 0] ]) */ -export function dft (notes) { - var pcs = pcset(notes) - return [0, 1, 2, 3, 4, 5, 6].map(function (n) { - return truncate(component(n, pcs)) - }) +export function dft(notes) { + var pcs = pcset(notes); + return [0, 1, 2, 3, 4, 5, 6].map(function(n) { + return truncate(component(n, pcs)); + }); } /** * Get the nth component of a given pitch class set * @private */ -function component (n, pcs) { - return pcs.reduce(function (complex, p) { - // calculate the complex number for n - var v = 2 * PI * p * n / 12 - complex[0] += cos(v) - complex[1] += sin(v) - return complex - }, [0, 0]) +function component(n, pcs) { + return pcs.reduce( + function(complex, p) { + // calculate the complex number for n + var v = 2 * PI * p * n / 12; + complex[0] += cos(v); + complex[1] += sin(v); + return complex; + }, + [0, 0] + ); } -var MIN = 1e-10 +var MIN = 1e-10; /** * Set 0 very small numbers in a complex number * @private */ -function truncate (cpx) { - if (cpx[0] < MIN) cpx[0] = 0 - if (cpx[1] < MIN) cpx[1] = 0 - return cpx +function truncate(cpx) { + if (cpx[0] < MIN) cpx[0] = 0; + if (cpx[1] < MIN) cpx[1] = 0; + return cpx; } /** @@ -87,11 +92,11 @@ function truncate (cpx) { * @example * dft.spectra('C E G#') // => [3, 0, 0, 3, 0, 0, 3] */ -export function spectra (notes) { - var comp = dft(notes) - return comp.map(function (complex) { - return sqrt(pow(complex[0], 2) + pow(complex[1], 2)) - }) +export function spectra(notes) { + var comp = dft(notes); + return comp.map(function(complex) { + return sqrt(pow(complex[0], 2) + pow(complex[1], 2)); + }); } /** @@ -101,10 +106,12 @@ export function spectra (notes) { * @param {String|Array} set2 - the second pitch class set or notes * @return the Euclidean distance between both */ -export function distance (set1, set2) { - var sp1 = spectra(set1) - var sp2 = spectra(set2) - return Math.sqrt(sp1.reduce(function (v, _, i) { - return v + Math.pow(sp1[i] - sp2[i], 2) - }, 0)) +export function distance(set1, set2) { + var sp1 = spectra(set1); + var sp2 = spectra(set2); + return Math.sqrt( + sp1.reduce(function(v, _, i) { + return v + Math.pow(sp1[i] - sp2[i], 2); + }, 0) + ); } diff --git a/packages/incubator/pcset-dft/test/pcset-dft.test.js b/packages/incubator/pcset-dft/test/pcset-dft.test.js index 40aaeb8..8298ab1 100644 --- a/packages/incubator/pcset-dft/test/pcset-dft.test.js +++ b/packages/incubator/pcset-dft/test/pcset-dft.test.js @@ -1,20 +1,28 @@ /* global describe test expect */ -var dft = require('..') +var dft = require(".."); -describe('tonal-pcset-dft', () => { - test('pcset', () => { - expect(dft.pcset('C4 E4 G#4')).toEqual([ '0', '4', '8' ]) - }) +describe("tonal-pcset-dft", () => { + test("pcset", () => { + expect(dft.pcset("C4 E4 G#4")).toEqual(["0", "4", "8"]); + }); - test('components', () => { - expect(dft.dft('C4 E4 G#4')).toEqual([ [ 3, 0 ], [ 0, 0 ], [ 0, 0 ], [ 3, 0 ], [ 0, 0 ], [ 0, 0 ], [ 3, 0 ] ]) - }) + test("components", () => { + expect(dft.dft("C4 E4 G#4")).toEqual([ + [3, 0], + [0, 0], + [0, 0], + [3, 0], + [0, 0], + [0, 0], + [3, 0] + ]); + }); - test('spectra', () => { - expect(dft.spectra('C4 E4 G#4')).toEqual([ 3, 0, 0, 3, 0, 0, 3 ]) - }) + test("spectra", () => { + expect(dft.spectra("C4 E4 G#4")).toEqual([3, 0, 0, 3, 0, 0, 3]); + }); - test('distance', () => { - expect(dft.distance('C E G', 'C Eb G')).toBe(1.5307337294603596) - }) -}) + test("distance", () => { + expect(dft.distance("C E G", "C Eb G")).toBe(1.5307337294603596); + }); +}); diff --git a/packages/incubator/pitchset/index.js b/packages/incubator/pitchset/index.js index e6d8020..9ce59cb 100644 --- a/packages/incubator/pitchset/index.js +++ b/packages/incubator/pitchset/index.js @@ -6,7 +6,7 @@ * * @module pitchset */ -import { sort } from 'tonal-array' +import { sort } from "tonal-array"; /** * Get the notes of a pitch set. The notes in the set are sorted in asceding @@ -20,8 +20,8 @@ import { sort } from 'tonal-array' * @example * pitchset.notes('C4 c3 C5 c4') // => ['C3', 'C4', 'C5'] */ -export function notes (notes) { - return sort(notes).filter(function (n, i, arr) { - return i === 0 || n !== arr[i - 1] - }) +export function notes(notes) { + return sort(notes).filter(function(n, i, arr) { + return i === 0 || n !== arr[i - 1]; + }); } diff --git a/packages/incubator/pitchset/test/pitchset.test.js b/packages/incubator/pitchset/test/pitchset.test.js index 28ac929..be0b58a 100644 --- a/packages/incubator/pitchset/test/pitchset.test.js +++ b/packages/incubator/pitchset/test/pitchset.test.js @@ -1,8 +1,8 @@ /* global describe test expect */ -var pitchset = require('..') +var pitchset = require(".."); -describe('tonal-pitchset', () => { - test('notes', () => { - expect(pitchset.notes('C4 c3 C5 C4 c4')).toEqual(['C3', 'C4', 'C5']) - }) -}) +describe("tonal-pitchset", () => { + test("notes", () => { + expect(pitchset.notes("C4 c3 C5 C4 c4")).toEqual(["C3", "C4", "C5"]); + }); +}); diff --git a/packages/incubator/sonority/index.js b/packages/incubator/sonority/index.js index 6c84d3b..fe4d2e2 100644 --- a/packages/incubator/sonority/index.js +++ b/packages/incubator/sonority/index.js @@ -2,9 +2,9 @@ * * @module sonority */ -import { ic } from 'tonal-interval' -import { asNotePitch, chr } from 'tonal-pitch' -import { map, compact } from 'tonal-array' +import { ic } from "tonal-interval"; +import { asNotePitch, chr } from "tonal-pitch"; +import { map, compact } from "tonal-array"; /** * Get the intervals analysis of a collection of notes @@ -32,17 +32,17 @@ import { map, compact } from 'tonal-array' * @param {Array|String} notes - the notes to analyze * @return {Array} the _pmnsdt_ array */ -export function density (list) { - var a, b, i - var notes = compact(map(asNotePitch, list)) - var len = notes.length - var result = [0, 0, 0, 0, 0, 0] +export function density(list) { + var a, b, i; + var notes = compact(map(asNotePitch, list)); + var len = notes.length; + var result = [0, 0, 0, 0, 0, 0]; for (a = 0; a < len; a++) { for (b = a; b < len; b++) { - i = ic(chr(notes[b]) - chr(notes[a])) - if (i === 6) result[5] = result[5] + 1 - else if (i > 0) result[5 - i] = result[5 - i] + 1 + i = ic(chr(notes[b]) - chr(notes[a])); + if (i === 6) result[5] = result[5] + 1; + else if (i > 0) result[5 - i] = result[5 - i] + 1; } } - return result + return result; } diff --git a/packages/incubator/sonority/test/sonority.test.js b/packages/incubator/sonority/test/sonority.test.js index 780051f..e6b0ef2 100644 --- a/packages/incubator/sonority/test/sonority.test.js +++ b/packages/incubator/sonority/test/sonority.test.js @@ -1,9 +1,9 @@ /* global describe test expect */ -var sonority = require('..') +var sonority = require(".."); -describe('tonal-sonority', () => { - test('density', () => { - expect(sonority.density('c e g b')).toEqual([ 2, 2, 1, 0, 1, 0 ]) - expect(sonority.density('c d gb')).toEqual([ 0, 1, 0, 1, 0, 1 ]) - }) -}) +describe("tonal-sonority", () => { + test("density", () => { + expect(sonority.density("c e g b")).toEqual([2, 2, 1, 0, 1, 0]); + expect(sonority.density("c d gb")).toEqual([0, 1, 0, 1, 0, 1]); + }); +}); diff --git a/packages/incubator/spell/index.js b/packages/incubator/spell/index.js index a609a9c..97be4f4 100644 --- a/packages/incubator/spell/index.js +++ b/packages/incubator/spell/index.js @@ -1,4 +1,4 @@ -var { parse, midi, chroma } = require('note-parser') +var { parse, midi, chroma } = require("note-parser"); /** * This module contains functions to convert from numeric pitch to @@ -14,52 +14,54 @@ var { parse, midi, chroma } = require('note-parser') * @module spell */ -export function pitch (note) { - return typeof note === 'number' ? note : midi(note) || chroma(note) +export function pitch(note) { + return typeof note === "number" ? note : midi(note) || chroma(note); } -var LETTERS = ['C', null, 'D', null, 'E', 'F', null, 'G', null, 'A', null, 'B'] +var LETTERS = ["C", null, "D", null, "E", "F", null, "G", null, "A", null, "B"]; -function pc (midi) { - var pc = midi % 12 - return pc < 0 ? 12 + pc : pc +function pc(midi) { + var pc = midi % 12; + return pc < 0 ? 12 + pc : pc; +} +function letter(midi) { + return LETTERS[pc(midi)]; } -function letter (midi) { return LETTERS[pc(midi)] } -export function pcnote (midi, base) { - base = base || 0 - var l = letter(midi) - if (l) return l - var prev = letter(midi - 1) - var next = letter(midi + 1) - var prevDist = Math.abs(fff(prev, 1) - base) - var nextDist = Math.abs(fff(next, -1) - base) +export function pcnote(midi, base) { + base = base || 0; + var l = letter(midi); + if (l) return l; + var prev = letter(midi - 1); + var next = letter(midi + 1); + var prevDist = Math.abs(fff(prev, 1) - base); + var nextDist = Math.abs(fff(next, -1) - base); // console.log('ALT', prev + '#', prevDist, next + 'b', nextDist) - return prevDist < nextDist ? prev + '#' : next + 'b' + return prevDist < nextDist ? prev + "#" : next + "b"; } // fifths distance for [A, B, C, D, E, F, G] -var FIFTHS = [4, 6, 1, 3, 5, 0, 2] -function fff (letter, alt) { - return FIFTHS[letter.charCodeAt(0) - 65] + alt * 7 +var FIFTHS = [4, 6, 1, 3, 5, 0, 2]; +function fff(letter, alt) { + return FIFTHS[letter.charCodeAt(0) - 65] + alt * 7; } -export function fifths (name) { - var props = parse(name) - return fff(props.letter, props.alt) +export function fifths(name) { + var props = parse(name); + return fff(props.letter, props.alt); } -export var note = noteIn('C') +export var note = noteIn("C"); -export function noteIn (key, midi) { - var base = typeof key === 'number' ? key : fifths(key) - return function (midi) { - return pcnote(midi, base) + ((midi - midi % 12) / 12 - 1) - } +export function noteIn(key, midi) { + var base = typeof key === "number" ? key : fifths(key); + return function(midi) { + return pcnote(midi, base) + ((midi - midi % 12) / 12 - 1); + }; } -export function pcIn (key, midi) { - var base = typeof key === 'number' ? key : fifths(key) - return function (midi) { - return pcnote(midi, base) - } +export function pcIn(key, midi) { + var base = typeof key === "number" ? key : fifths(key); + return function(midi) { + return pcnote(midi, base); + }; } diff --git a/packages/incubator/spell/test/spell.test.js b/packages/incubator/spell/test/spell.test.js index ce862dc..f0e489d 100644 --- a/packages/incubator/spell/test/spell.test.js +++ b/packages/incubator/spell/test/spell.test.js @@ -1,41 +1,88 @@ /* global describe test expect */ -var spell = require('..') +var spell = require(".."); -describe('tonal-spell', () => { - test('get pitch class names', () => { - expect(spell.pcnote(60)).toBe('C') - expect(spell.pcnote(70)).toBe('Bb') - expect(spell.pcnote(73)).toBe('Db') - }) +describe("tonal-spell", () => { + test("get pitch class names", () => { + expect(spell.pcnote(60)).toBe("C"); + expect(spell.pcnote(70)).toBe("Bb"); + expect(spell.pcnote(73)).toBe("Db"); + }); - test('get fifths from F', () => { - expect('F C G D A E B'.split(' ').map(spell.fifths)) - .toEqual([ 0, 1, 2, 3, 4, 5, 6 ]) - expect('F# C# G# D# A# E# B#'.split(' ').map(spell.fifths)) - .toEqual([ 7, 8, 9, 10, 11, 12, 13 ]) - expect('F## C## G## D## A## E## B##'.split(' ').map(spell.fifths)) - .toEqual([ 14, 15, 16, 17, 18, 19, 20 ]) - expect('Bb Eb Ab Db Gb Cb Fb'.split(' ').map(spell.fifths)) - .toEqual([ -1, -2, -3, -4, -5, -6, -7 ]) - expect('Bbb Ebb Abb Dbb Gbb Cbb Fbb'.split(' ').map(spell.fifths)) - .toEqual([ -8, -9, -10, -11, -12, -13, -14 ]) - }) + test("get fifths from F", () => { + expect("F C G D A E B".split(" ").map(spell.fifths)).toEqual([ + 0, + 1, + 2, + 3, + 4, + 5, + 6 + ]); + expect("F# C# G# D# A# E# B#".split(" ").map(spell.fifths)).toEqual([ + 7, + 8, + 9, + 10, + 11, + 12, + 13 + ]); + expect("F## C## G## D## A## E## B##".split(" ").map(spell.fifths)).toEqual([ + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ]); + expect("Bb Eb Ab Db Gb Cb Fb".split(" ").map(spell.fifths)).toEqual([ + -1, + -2, + -3, + -4, + -5, + -6, + -7 + ]); + expect("Bbb Ebb Abb Dbb Gbb Cbb Fbb".split(" ").map(spell.fifths)).toEqual([ + -8, + -9, + -10, + -11, + -12, + -13, + -14 + ]); + }); - test.skip('get note names', () => { - var notes = 'C D E F G A B'.split(' ') - var major = [0, 2, 4, 5, 7, 9, 11] - var chromatic = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - var tr = (b) => (x) => x + b - var scale = (type) => (root) => type.map(tr(spell.pitch(root) + 60)).map(spell.pcIn(root)).join(' ') + test.skip("get note names", () => { + var notes = "C D E F G A B".split(" "); + var major = [0, 2, 4, 5, 7, 9, 11]; + var chromatic = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + var tr = b => x => x + b; + var scale = type => root => + type + .map(tr(spell.pitch(root) + 60)) + .map(spell.pcIn(root)) + .join(" "); expect(notes.map(scale(major))).toEqual([ - 'C D E F G A B', 'D E F# G A B C#', 'E F# G# A B C# D#', 'F G A Bb C D E', - 'G A B C D E F#', 'A B C# D E F# G#', 'B C# D# E F# G# A#' - ]) + "C D E F G A B", + "D E F# G A B C#", + "E F# G# A B C# D#", + "F G A Bb C D E", + "G A B C D E F#", + "A B C# D E F# G#", + "B C# D# E F# G# A#" + ]); expect(notes.map(scale(chromatic))).toEqual([ - 'C Db D Eb E F Gb G Ab A Bb B', 'D Eb E F F# G G# A Bb B C C#', - 'E F F# G G# A Bb B C C# D D#', 'F Gb G Ab A Bb B C Db D Eb E', - 'G Ab A Bb B C C# D Eb E F F#', 'A Bb B C C# D D# E F F# G G#', - 'B C C# D D# E F F# G G# A A#' - ]) - }) -}) + "C Db D Eb E F Gb G Ab A Bb B", + "D Eb E F F# G G# A Bb B C C#", + "E F F# G G# A Bb B C C# D D#", + "F Gb G Ab A Bb B C Db D Eb E", + "G Ab A Bb B C C# D Eb E F F#", + "A Bb B C C# D D# E F F# G G#", + "B C C# D D# E F F# G G# A A#" + ]); + }); +}); diff --git a/packages/incubator/triad/index.js b/packages/incubator/triad/index.js index 1b41546..d8fed62 100644 --- a/packages/incubator/triad/index.js +++ b/packages/incubator/triad/index.js @@ -4,17 +4,15 @@ * * @module triad */ -import { permutations } from 'tonal-array' -import { transpose } from 'tonal-transpose' +import { permutations } from "tonal-array"; +import { transpose } from "tonal-transpose"; /** * Given a scale, return a triadic structure starting from the root of the scale * For example, given "c d e f g a b" returns "Mm" * @param {Array} scale - the scale */ -export function fromScale (scale, steps = 2) { - -} +export function fromScale(scale, steps = 2) {} /** * Given a triad structure, return the intervals @@ -25,20 +23,23 @@ export function fromScale (scale, steps = 2) { * @example * triad.intervals('Mmm') // => [ '1P', '3M', '5P', '7m' ] */ -export function intervals (st) { - var inner = st.split('').map((t) => t === 'm' ? '3m' : '3M') - return inner.reduce(function (ivls, v) { - ivls.push(transpose(v, ivls[ivls.length - 1])) - return ivls - }, ['1P']) +export function intervals(st) { + var inner = st.split("").map(t => (t === "m" ? "3m" : "3M")); + return inner.reduce( + function(ivls, v) { + ivls.push(transpose(v, ivls[ivls.length - 1])); + return ivls; + }, + ["1P"] + ); } -export function allFor (st) { - return sortedSet(permutations(st.split('')).map((s) => s.join(''))) +export function allFor(st) { + return sortedSet(permutations(st.split("")).map(s => s.join(""))); } -function sortedSet (arr) { - return arr.sort().filter(function (v, i) { - return i === 0 || v !== arr[i - 1] - }) +function sortedSet(arr) { + return arr.sort().filter(function(v, i) { + return i === 0 || v !== arr[i - 1]; + }); } diff --git a/packages/incubator/triad/test/tertian.test.js b/packages/incubator/triad/test/tertian.test.js index 71031e1..16d79ad 100644 --- a/packages/incubator/triad/test/tertian.test.js +++ b/packages/incubator/triad/test/tertian.test.js @@ -1,25 +1,35 @@ /* global describe test expect */ -var tertian = require('..') +var tertian = require(".."); -describe('tonal-scale', () => { - test('tertian: intervals', () => { - expect(tertian.intervals('mm')).toEqual([ '1P', '3m', '5d' ]) - expect(tertian.intervals('Mm')).toEqual([ '1P', '3M', '5P' ]) - expect(tertian.intervals('Mmm')).toEqual([ '1P', '3M', '5P', '7m' ]) - }) +describe("tonal-scale", () => { + test("tertian: intervals", () => { + expect(tertian.intervals("mm")).toEqual(["1P", "3m", "5d"]); + expect(tertian.intervals("Mm")).toEqual(["1P", "3M", "5P"]); + expect(tertian.intervals("Mmm")).toEqual(["1P", "3M", "5P", "7m"]); + }); - test('tertian: permutations', () => { - expect(tertian.allFor('mmMM')) - .toEqual([ 'MMmm', 'MmMm', 'MmmM', 'mMMm', 'mMmM', 'mmMM' ]) + test("tertian: permutations", () => { + expect(tertian.allFor("mmMM")).toEqual([ + "MMmm", + "MmMm", + "MmmM", + "mMMm", + "mMmM", + "mmMM" + ]); - expect(tertian.allFor('mmMM').map(tertian.intervals).map(i => i.join(' '))) - .toEqual([ - '1P 3M 5A 7M 9M', - '1P 3M 5P 7M 9M', - '1P 3M 5P 7m 9M', - '1P 3m 5P 7M 9M', - '1P 3m 5P 7m 9M', - '1P 3m 5d 7m 9M' - ]) - }) -}) + expect( + tertian + .allFor("mmMM") + .map(tertian.intervals) + .map(i => i.join(" ")) + ).toEqual([ + "1P 3M 5A 7M 9M", + "1P 3M 5P 7M 9M", + "1P 3M 5P 7m 9M", + "1P 3m 5P 7M 9M", + "1P 3m 5P 7m 9M", + "1P 3m 5d 7m 9M" + ]); + }); +}); diff --git a/packages/tonal/README.md b/packages/tonal/README.md index f7dd6ab..97bb1f7 100644 --- a/packages/tonal/README.md +++ b/packages/tonal/README.md @@ -37,7 +37,7 @@ tonal.interval('C', 'G') // => '5P' tonal.semitones('C', 'G') // => 7 // scales -tonal.scale('Bb lydian') // => [ 'Ab', 'Bb', 'C', 'D', 'Eb', 'F', 'G' ] +tonal.scale('Bb lydian') // => [ 'Bb', 'C', 'D', 'E', 'F', 'G', 'A'] tonal.scale('Eb bebop') // => [ 'Eb', 'F', 'G', 'Ab', 'Bb', 'C', 'Db', 'D' ] tonal.scale.names() tonal.scale.detect('Bb4 Eb4 C5 G4 Bb4 F6') // => ['Eb major pentatonic'] @@ -134,7 +134,7 @@ Browser: grab the minified file [here](https://github.com/danigb/tonal/blob/mast ES6: ```js -import tonal from 'tonal' +import tonal from 'tonal' tonal.transpose('C4', '3M') ``` @@ -142,7 +142,7 @@ ES5: ```js var tonal = require('tonal') -tonal.tranpose('C4', '2m') +tonal.transpose('C4', '2m') ``` Browser (use the `Tonal` global object): @@ -160,7 +160,7 @@ Mostly, because I want to learn: > Reinventing the wheel is bad for business, but it’s great for learning [*](http://philipwalton.com/articles/how-to-become-a-great-front-end-engineer) -I want to learn about music theory and I want to express the conpcets I learn using functional programming style. +I want to learn about music theory and I want to express the concepts I learn using functional programming style. Also, I want a complete library, where I can model some (for me) esoteric features like [interval classes](http://danigb.github.io/tonal/api/module-interval.html#.ic), pitch sets, dft to pitch class sets, and so on. diff --git a/packages/tonal/chord/index.js b/packages/tonal/chord/index.js index 58d94da..34f365a 100644 --- a/packages/tonal/chord/index.js +++ b/packages/tonal/chord/index.js @@ -11,14 +11,16 @@ * * @module chord */ -import { dictionary, detector } from 'tonal-dictionary' -import { map, compact, permutations, rotate } from 'tonal-array' -import { pc, name as note } from 'tonal-note' -import { regex } from 'note-parser' -import { harmonize, intervallic } from 'tonal-harmonizer' -import DATA from './chords.json' +import { dictionary, detector } from "tonal-dictionary"; +import { map, compact, permutations, rotate } from "tonal-array"; +import { pc, name as note } from "tonal-note"; +import { regex } from "note-parser"; +import { harmonize, intervallic } from "tonal-harmonizer"; +import DATA from "./chords.json"; -var dict = dictionary(DATA, function (str) { return str.split(' ') }) +var dict = dictionary(DATA, function(str) { + return str.split(" "); +}); /** * Return the available chord names @@ -31,7 +33,7 @@ var dict = dictionary(DATA, function (str) { return str.split(' ') }) * var chord = require('tonal-chord') * chord.names() // => ['maj7', ...] */ -export var names = dict.keys +export var names = dict.keys; /** * Get chord notes or intervals from chord type @@ -47,10 +49,13 @@ export var names = dict.keys * maj7 = chords.get('Maj7') * maj7('C') // => ['C', 'E', 'G', 'B'] */ -export function get (type, tonic) { - if (arguments.length === 1) return function (t) { return get(type, t) } - var ivls = dict.get(type) - return ivls ? harmonize(ivls, tonic) : null +export function get(type, tonic) { + if (arguments.length === 1) + return function(t) { + return get(type, t); + }; + var ivls = dict.get(type); + return ivls ? harmonize(ivls, tonic) : null; } /** @@ -65,10 +70,10 @@ export function get (type, tonic) { * @example * chord.notes('Cmaj7') // => ['C', 'E', 'G', 'B'] */ -export function notes (chord) { - var p = parse(chord) - var ivls = dict.get(p.type) - return ivls ? harmonize(ivls, p.tonic) : compact(map(note, chord)) +export function notes(chord) { + var p = parse(chord); + var ivls = dict.get(p.type); + return ivls ? harmonize(ivls, p.tonic) : compact(map(note, chord)); } /** @@ -77,9 +82,9 @@ export function notes (chord) { * @param {String} name - the chord name (optionally a tonic and type) * @return {Array} a list of intervals or null if the type is not known */ -export function intervals (name) { - var p = parse(name) - return dict.get(p.type) || [] +export function intervals(name) { + var p = parse(name); + return dict.get(p.type) || []; } /** @@ -91,8 +96,8 @@ export function intervals (name) { * chord.isKnownChord('Maj7') // => true * chord.isKnownChord('Ablah') // => false */ -export function isKnownChord (name) { - return intervals(name).length > 0 +export function isKnownChord(name) { + return intervals(name).length > 0; } /** @@ -106,7 +111,7 @@ export function isKnownChord (name) { * chord.detect('b g f# d') // => [ 'GMaj7' ] * chord.detect('e c a g') // => [ 'CM6', 'Am7' ] */ -export var detect = detector(dict, '') +export var detect = detector(dict, ""); /** * Get the position (inversion number) of a chord (0 is root position, 1 is first @@ -120,10 +125,10 @@ export var detect = detector(dict, '') * chord.position('e g c') // => 1 * chord.position('g3 e2 c5') // => 1 (e is the lowest note) */ -export function position (chord) { - var pcs = map(pc, chord) - var sorted = sortTriads(pcs) - return sorted ? sorted.indexOf(pcs[0]) : null +export function position(chord) { + var pcs = map(pc, chord); + var sorted = sortTriads(pcs); + return sorted ? sorted.indexOf(pcs[0]) : null; } /** @@ -140,26 +145,29 @@ export function position (chord) { * chord.inversion(1, 'Cmaj7') // => [ 'E', 'G', 'B', 'C' ] * chord.inversion(0, 'e g c') // => [ 'C', 'E', 'G' ] */ -export function inversion (num, chord) { - if (arguments.length === 1) return function (c) { return inversion(num, c) } - var sorted = sortTriads(chord) - return sorted ? rotate(num, sorted) : [] +export function inversion(num, chord) { + if (arguments.length === 1) + return function(c) { + return inversion(num, c); + }; + var sorted = sortTriads(chord); + return sorted ? rotate(num, sorted) : []; } -function sortTriads (chord) { - var all = permutations(notes(chord).map(pc)) +function sortTriads(chord) { + var all = permutations(notes(chord).map(pc)); for (var i = 0; i < all.length; i++) { - var ivls = intervallic(all[i]) - if (areTriads(ivls)) return all[i] + var ivls = intervallic(all[i]); + if (areTriads(ivls)) return all[i]; } - return null + return null; } -function areTriads (list) { +function areTriads(list) { for (var i = 0; i < list.length; i++) { - if (list[i][0] !== '3') return false + if (list[i][0] !== "3") return false; } - return true + return true; } /** @@ -178,13 +186,15 @@ function areTriads (list) { * chord.parse('mMaj7') // => { tonic: false, type: 'mMaj7' } * chord.parse('Cnonsense') // => { tonic: 'C', type: 'nonsense' } */ -export function parse (name) { - var p = regex().exec(name) - if (!p) return { type: name, tonic: false } +export function parse(name) { + var p = regex().exec(name); + if (!p) return { type: name, tonic: false }; // If chord name is empty, the octave is the chord name - return !p[4] ? { type: p[3], tonic: p[1] + p[2] } - // If the octave is 6 or 7 is asumed to be part of the chord name - : (p[3] === '7' || p[3] === '6') ? { type: p[3] + p[4], tonic: p[1] + p[2] } - : { type: p[4], tonic: p[1] + p[2] + p[3] } + return !p[4] + ? { type: p[3], tonic: p[1] + p[2] } + : // If the octave is 6 or 7 is asumed to be part of the chord name + p[3] === "7" || p[3] === "6" + ? { type: p[3] + p[4], tonic: p[1] + p[2] } + : { type: p[4], tonic: p[1] + p[2] + p[3] }; } diff --git a/packages/tonal/chord/test/chord.test.js b/packages/tonal/chord/test/chord.test.js index 379281d..7c78223 100644 --- a/packages/tonal/chord/test/chord.test.js +++ b/packages/tonal/chord/test/chord.test.js @@ -1,79 +1,81 @@ /* global describe test expect */ -var chord = require('..') -var DATA = require('../chords.json') +var chord = require(".."); +var DATA = require("../chords.json"); -describe('tonal-chord', () => { - test('detect', () => { - expect(chord.detect('c e g b')).toEqual([ 'CMaj7' ]) - expect(chord.detect('e c a g')).toEqual([ 'CM6', 'Am7' ]) - expect(chord.detect('g d f# b')).toEqual([ 'GMaj7' ]) - expect(chord.detect('f a d g b')).toEqual([ 'Dm6', 'G9' ]) - expect(chord.detect('f bb g d# a')).toEqual([ 'Gm9#5' ]) - }) +describe("tonal-chord", () => { + test("detect", () => { + expect(chord.detect("c e g b")).toEqual(["CMaj7"]); + expect(chord.detect("e c a g")).toEqual(["CM6", "Am7"]); + expect(chord.detect("g d f# b")).toEqual(["GMaj7"]); + expect(chord.detect("f a d g b")).toEqual(["Dm6", "G9"]); + expect(chord.detect("f bb g d# a")).toEqual(["Gm9#5"]); + }); - test('chord data integrity', () => { - chord.names(true).forEach(function (name) { - if (!Array.isArray(DATA[name])) return - var data = chord.get(name, false) - var filtered = data.filter(function (x) { return x }) - expect(data.length).toBe(filtered.length) - }) - }) + test("chord data integrity", () => { + chord.names(true).forEach(function(name) { + if (!Array.isArray(DATA[name])) return; + var data = chord.get(name, false); + var filtered = data.filter(function(x) { + return x; + }); + expect(data.length).toBe(filtered.length); + }); + }); - test('parse', () => { - expect(chord.parse('Cmaj7')).toEqual({ type: 'maj7', tonic: 'C' }) - expect(chord.parse('C7')).toEqual({ type: '7', tonic: 'C' }) - expect(chord.parse('maj7')).toEqual({ type: 'maj7', tonic: false }) - expect(chord.parse('C#4 m7b5')).toEqual({ type: 'm7b5', tonic: 'C#4' }) - expect(chord.parse('C#4m7b5')).toEqual({ type: 'm7b5', tonic: 'C#4' }) - expect(chord.parse('Cb7b5')).toEqual({ type: '7b5', tonic: 'Cb' }) - expect(chord.parse('Eb7add6')).toEqual({ tonic: 'Eb', type: '7add6' }) - expect(chord.parse('Bb6b5')).toEqual({ tonic: 'Bb', type: '6b5' }) - }) + test("parse", () => { + expect(chord.parse("Cmaj7")).toEqual({ type: "maj7", tonic: "C" }); + expect(chord.parse("C7")).toEqual({ type: "7", tonic: "C" }); + expect(chord.parse("maj7")).toEqual({ type: "maj7", tonic: false }); + expect(chord.parse("C#4 m7b5")).toEqual({ type: "m7b5", tonic: "C#4" }); + expect(chord.parse("C#4m7b5")).toEqual({ type: "m7b5", tonic: "C#4" }); + expect(chord.parse("Cb7b5")).toEqual({ type: "7b5", tonic: "Cb" }); + expect(chord.parse("Eb7add6")).toEqual({ tonic: "Eb", type: "7add6" }); + expect(chord.parse("Bb6b5")).toEqual({ tonic: "Bb", type: "6b5" }); + }); - test('get', () => { - expect(chord.get('maj7#5', 'D')).toEqual([ 'D', 'F#', 'A#', 'C#' ]) - expect(chord.get('m7')('Db')).toEqual(['Db', 'Fb', 'Ab', 'Cb']) - }) + test("get", () => { + expect(chord.get("maj7#5", "D")).toEqual(["D", "F#", "A#", "C#"]); + expect(chord.get("m7")("Db")).toEqual(["Db", "Fb", "Ab", "Cb"]); + }); - test('notes', () => { - expect(chord.notes('Cmaj7')).toEqual([ 'C', 'E', 'G', 'B' ]) - expect(chord.notes('Eb7add6')).toEqual(['Eb', 'G', 'Bb', 'Db', 'C']) - expect(chord.notes('C4 maj7')).toEqual([ 'C4', 'E4', 'G4', 'B4' ]) - expect(chord.notes('C7')).toEqual([ 'C', 'E', 'G', 'Bb' ]) - expect(chord.notes('C64')).toEqual(['G', 'C', 'E']) - expect(chord.notes('Cmaj7#5')).toEqual([ 'C', 'E', 'G#', 'B' ]) - expect(chord.notes('e4 c5 g2')).toEqual(['E4', 'C5', 'G2']) - expect(chord.notes('blah')).toEqual([]) - }) + test("notes", () => { + expect(chord.notes("Cmaj7")).toEqual(["C", "E", "G", "B"]); + expect(chord.notes("Eb7add6")).toEqual(["Eb", "G", "Bb", "Db", "C"]); + expect(chord.notes("C4 maj7")).toEqual(["C4", "E4", "G4", "B4"]); + expect(chord.notes("C7")).toEqual(["C", "E", "G", "Bb"]); + expect(chord.notes("C64")).toEqual(["G", "C", "E"]); + expect(chord.notes("Cmaj7#5")).toEqual(["C", "E", "G#", "B"]); + expect(chord.notes("e4 c5 g2")).toEqual(["E4", "C5", "G2"]); + expect(chord.notes("blah")).toEqual([]); + }); - test('intervals', () => { - expect(chord.intervals('Cmaj7')).toEqual(['1P', '3M', '5P', '7M']) - expect(chord.intervals('major')).toEqual([]) - }) + test("intervals", () => { + expect(chord.intervals("Cmaj7")).toEqual(["1P", "3M", "5P", "7M"]); + expect(chord.intervals("major")).toEqual([]); + }); - test('isKnownChord', () => { - expect(chord.isKnownChord('maj7')).toBe(true) - expect(chord.isKnownChord('major')).toBe(false) - }) + test("isKnownChord", () => { + expect(chord.isKnownChord("maj7")).toBe(true); + expect(chord.isKnownChord("major")).toBe(false); + }); - test('position', () => { - expect(chord.position('g2 c3 e4 b')).toEqual(2) - expect(chord.position('b e c g')).toEqual(3) - }) + test("position", () => { + expect(chord.position("g2 c3 e4 b")).toEqual(2); + expect(chord.position("b e c g")).toEqual(3); + }); - test('inversion', () => { - expect(chord.inversion(1, 'C4 maj7')).toEqual([ 'E', 'G', 'B', 'C' ]) - expect(chord.inversion(0, 'e g c')).toEqual([ 'C', 'E', 'G' ]) - expect(chord.inversion(1, 'e g c')).toEqual([ 'E', 'G', 'C' ]) - expect(chord.inversion(2, 'e g c')).toEqual([ 'G', 'C', 'E' ]) - expect(chord.inversion(0)('b g e d c')).toEqual([ 'C', 'E', 'G', 'B', 'D' ]) - expect(chord.inversion(3, 'CMaj7#5')).toEqual([ 'B', 'C', 'E', 'G#' ]) - expect(chord.inversion(1, 'c d e')).toEqual([]) - }) + test("inversion", () => { + expect(chord.inversion(1, "C4 maj7")).toEqual(["E", "G", "B", "C"]); + expect(chord.inversion(0, "e g c")).toEqual(["C", "E", "G"]); + expect(chord.inversion(1, "e g c")).toEqual(["E", "G", "C"]); + expect(chord.inversion(2, "e g c")).toEqual(["G", "C", "E"]); + expect(chord.inversion(0)("b g e d c")).toEqual(["C", "E", "G", "B", "D"]); + expect(chord.inversion(3, "CMaj7#5")).toEqual(["B", "C", "E", "G#"]); + expect(chord.inversion(1, "c d e")).toEqual([]); + }); - test('names', () => { - test(chord.names().length > 0) - test(chord.names(true).length > chord.names().length) - }) -}) + test("names", () => { + test(chord.names().length > 0); + test(chord.names(true).length > chord.names().length); + }); +}); diff --git a/packages/tonal/interval/index.js b/packages/tonal/interval/index.js index e88fcb0..9d89b4a 100644 --- a/packages/tonal/interval/index.js +++ b/packages/tonal/interval/index.js @@ -34,9 +34,17 @@ * * @module interval */ -import { build } from 'interval-notation' -import { asIvlPitch, ivlFn, chr, dir, - strIvl, encode, decode, height } from 'tonal-pitch' +import { build } from "interval-notation"; +import { + asIvlPitch, + ivlFn, + chr, + dir, + strIvl, + encode, + decode, + height +} from "tonal-pitch"; /** * Get interval name. Can be used to test if it's an interval. It accepts intervals @@ -49,9 +57,9 @@ import { asIvlPitch, ivlFn, chr, dir, * interval.toInterval('m-3') // => '-3m' * interval.toInterval('3') // => null */ -export function toInterval (ivl) { - var i = asIvlPitch(ivl) - return i ? strIvl(i) : null +export function toInterval(ivl) { + var i = asIvlPitch(ivl); + return i ? strIvl(i) : null; } /** @@ -64,9 +72,9 @@ export function toInterval (ivl) { * interval.num('P9') // => 9 * interval.num('P-4') // => 4 */ -export function num (ivl) { - var p = props(ivl) - return p ? p.num : null +export function num(ivl) { + var p = props(ivl); + return p ? p.num : null; } /** @@ -81,9 +89,9 @@ export function num (ivl) { * interval.num('P-4') // => -4 * interval.num('m-9') // => -9 */ -export function value (ivl) { - var p = props(ivl) - return p ? p.num * p.dir : null +export function value(ivl) { + var p = props(ivl); + return p ? p.num * p.dir : null; } /** @@ -101,11 +109,11 @@ export function value (ivl) { * interval.parse('P-4') // => { num: 4, alt: 0, dir: -1} * interval.parse('m-9') // => { num: 9, alt: -1, dir: -1 } */ -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 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] }; } /** @@ -118,11 +126,11 @@ export function props (ivl) { * - 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) +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); } /** @@ -135,15 +143,15 @@ export function fromProps (props) { * // or using tonal * tonal.semitones('P5') // => 7 */ -export function semitones (ivl) { - var i = asIvlPitch(ivl) - return i ? height(i) : null +export function semitones(ivl) { + var i = asIvlPitch(ivl); + return i ? height(i) : null; } // interval numbers -var IN = [1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7] +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(' ') +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 @@ -156,15 +164,15 @@ var IQ = 'P m M m M P d P m M m M'.split(' ') * // or using tonal * tonal.fromSemitones(-7) // => '-5P' */ -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 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]; } -var CLASSES = [0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1] +var CLASSES = [0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]; /** * Get the [interval class](https://en.wikipedia.org/wiki/Interval_class) * number of a given interval. @@ -183,13 +191,13 @@ var CLASSES = [0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1] * interval.ic('m6') // => 4 * ['P1', 'M2', 'M3', 'P4', 'P5', 'M6', 'M7'].map(ic) // => [0, 2, 4, 5, 5, 3, 1] */ -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 function ic(ivl) { + var i = asIvlPitch(ivl); + var s = i ? chr(i) : Math.round(ivl); + return isNaN(s) ? null : CLASSES[Math.abs(s) % 12]; } -var TYPES = 'PMMPPMM' +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. @@ -200,9 +208,9 @@ var TYPES = 'PMMPPMM' * @example * interval.type('5A') // => 'P' */ -export function type (ivl) { - var i = asIvlPitch(ivl) - return i ? TYPES[decode(i)[0]] : null +export function type(ivl) { + var i = asIvlPitch(ivl); + return i ? TYPES[decode(i)[0]] : null; } /** @@ -218,13 +226,13 @@ export function type (ivl) { * interval.invert('3m') // => '6M' * interval.invert('2M') // => '7m' */ -export var invert = ivlFn(function (i) { - var d = decode(i) +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)) -}) + 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)); +}); /** * Get the simplified version of an interval. @@ -240,11 +248,11 @@ export var invert = ivlFn(function (i) { * interval.simplify('2M') // => '2M' * interval.simplify('-2M') // => '7m' */ -export var simplify = ivlFn(function (i) { +export var simplify = ivlFn(function(i) { // decode to [step, alt, octave] - var dec = decode(i) + var dec = decode(i); // if it's not 8 reduce the octaves to 0 - if (dec[0] !== 0 || dec[2] !== 1) dec[2] = 0 + if (dec[0] !== 0 || dec[2] !== 1) dec[2] = 0; // encode back - return encode(dec[0], dec[1], dec[2], dir(i)) -}) + return encode(dec[0], dec[1], dec[2], dir(i)); +}); diff --git a/packages/tonal/interval/test/interval.test.js b/packages/tonal/interval/test/interval.test.js index 7973ac0..2403fe3 100644 --- a/packages/tonal/interval/test/interval.test.js +++ b/packages/tonal/interval/test/interval.test.js @@ -1,98 +1,376 @@ /* global describe test expect */ -var ivl = require('..') +var ivl = require(".."); -function map (fn, arr) { - return (Array.isArray(arr) ? arr : arr.split(' ')).map(fn) +function map(fn, arr) { + return (Array.isArray(arr) ? arr : arr.split(" ")).map(fn); } -describe('tonal-interval', () => { - test('toInterval - get interval names', () => { - expect(map(ivl.toInterval, '1P 2M m3 P-4 5 blah')).toEqual([ '1P', '2M', '3m', '-4P', null, null ]) - }) +describe("tonal-interval", () => { + test("toInterval - get interval names", () => { + expect(map(ivl.toInterval, "1P 2M m3 P-4 5 blah")).toEqual([ + "1P", + "2M", + "3m", + "-4P", + null, + null + ]); + }); - test('toInterval - invalid intervals', () => { - expect(ivl.toInterval('blah')).toBe(null) - expect(ivl.toInterval('P9')).toBe(null) - }) + test("toInterval - invalid intervals", () => { + expect(ivl.toInterval("blah")).toBe(null); + expect(ivl.toInterval("P9")).toBe(null); + }); - test('props', () => { - expect(map(ivl.props, '1P 2M m2 m-2 P-4 5 blah M9 m9 M-9 -9m')).toEqual([ { alt: 0, dir: 1, num: 1 }, { alt: 0, dir: 1, num: 2 }, - { alt: -1, dir: 1, num: 2 }, { alt: -1, dir: -1, num: 2 }, - { alt: 0, dir: -1, num: 4 }, null, null, - { alt: 0, dir: 1, num: 9 }, { alt: -1, dir: 1, num: 9 }, - { alt: 0, dir: -1, num: 9 }, { alt: -1, dir: -1, num: 9 } - ]) - }) + test("props", () => { + expect(map(ivl.props, "1P 2M m2 m-2 P-4 5 blah M9 m9 M-9 -9m")).toEqual([ + { alt: 0, dir: 1, num: 1 }, + { alt: 0, dir: 1, num: 2 }, + { alt: -1, dir: 1, num: 2 }, + { alt: -1, dir: -1, num: 2 }, + { alt: 0, dir: -1, num: 4 }, + null, + null, + { alt: 0, dir: 1, num: 9 }, + { alt: -1, dir: 1, num: 9 }, + { alt: 0, dir: -1, num: 9 }, + { alt: -1, dir: -1, num: 9 } + ]); + }); - test('fromProps', () => { - expect(ivl.fromProps({ num: 1, alt: 0, dir: 1 })).toBe('1P') - expect(ivl.fromProps({ num: 8, alt: -1, dir: -1 })).toBe('-8d') - expect(ivl.fromProps({ num: 9, alt: -1, dir: -1 })).toBe('-9m') - expect(ivl.fromProps({ num: 0 })).toBe(null) - expect(ivl.fromProps({ num: -1 })).toBe(null) - expect(ivl.fromProps({ num: 7 })).toBe('7M') - expect(ivl.fromProps()).toBe(null) - }) + test("fromProps", () => { + expect(ivl.fromProps({ num: 1, alt: 0, dir: 1 })).toBe("1P"); + expect(ivl.fromProps({ num: 8, alt: -1, dir: -1 })).toBe("-8d"); + expect(ivl.fromProps({ num: 9, alt: -1, dir: -1 })).toBe("-9m"); + expect(ivl.fromProps({ num: 0 })).toBe(null); + expect(ivl.fromProps({ num: -1 })).toBe(null); + expect(ivl.fromProps({ num: 7 })).toBe("7M"); + expect(ivl.fromProps()).toBe(null); + }); - test('num', () => { - expect(map(ivl.num, '1P 2M m3 P-4 5 blah m11')).toEqual([1, 2, 3, 4, null, null, 11]) - }) + test("num", () => { + expect(map(ivl.num, "1P 2M m3 P-4 5 blah m11")).toEqual([ + 1, + 2, + 3, + 4, + null, + null, + 11 + ]); + }); - test('value', () => { - expect(map(ivl.value, '1P 2M m3 P-4 5 blah m-11')).toEqual([1, 2, 3, -4, null, null, -11]) - }) + test("value", () => { + expect(map(ivl.value, "1P 2M m3 P-4 5 blah m-11")).toEqual([ + 1, + 2, + 3, + -4, + null, + null, + -11 + ]); + }); - test('semitones', () => { - expect(map(ivl.semitones, '1P 2M 3M 4P 5P 6M 7M')).toEqual([ 0, 2, 4, 5, 7, 9, 11 ]) - expect(map(ivl.semitones, '8P 9M 10M 11P 12P 13M 14M')).toEqual([ 12, 14, 16, 17, 19, 21, 23 ]) - expect(map(ivl.semitones, '8d 9m 10m 11d 12d 13m 14m')).toEqual([ 11, 13, 15, 16, 18, 20, 22 ]) - expect(map(ivl.semitones, '-8P -9M -10M -11P -12P -13M -14M')).toEqual([ -12, -14, -16, -17, -19, -21, -23 ]) - }) + test("semitones", () => { + expect(map(ivl.semitones, "1P 2M 3M 4P 5P 6M 7M")).toEqual([ + 0, + 2, + 4, + 5, + 7, + 9, + 11 + ]); + expect(map(ivl.semitones, "8P 9M 10M 11P 12P 13M 14M")).toEqual([ + 12, + 14, + 16, + 17, + 19, + 21, + 23 + ]); + expect(map(ivl.semitones, "8d 9m 10m 11d 12d 13m 14m")).toEqual([ + 11, + 13, + 15, + 16, + 18, + 20, + 22 + ]); + expect(map(ivl.semitones, "-8P -9M -10M -11P -12P -13M -14M")).toEqual([ + -12, + -14, + -16, + -17, + -19, + -21, + -23 + ]); + }); - test('get interval name from semitones', () => { - expect(map(ivl.fromSemitones, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])).toEqual([ '1P', '2m', '2M', '3m', '3M', '4P', '5d', '5P', '6m', '6M', '7m', '7M' ]) - expect(map(ivl.fromSemitones, [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23])).toEqual( - [ '8P', '9m', '9M', '10m', '10M', '11P', '12d', '12P', '13m', '13M', '14m', '14M' ] - ) - expect(map(ivl.fromSemitones, [-0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11])).toEqual( - [ '1P', '-2m', '-2M', '-3m', '-3M', '-4P', '-5d', '-5P', '-6m', '-6M', '-7m', '-7M' ] - ) + test("get interval name from semitones", () => { expect( - map(ivl.fromSemitones, [-12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23]) - ).toEqual( - [ '-8P', '-9m', '-9M', '-10m', '-10M', '-11P', '-12d', '-12P', '-13m', '-13M', '-14m', '-14M' ] - ) - }) + map(ivl.fromSemitones, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + ).toEqual([ + "1P", + "2m", + "2M", + "3m", + "3M", + "4P", + "5d", + "5P", + "6m", + "6M", + "7m", + "7M" + ]); + expect( + map(ivl.fromSemitones, [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]) + ).toEqual([ + "8P", + "9m", + "9M", + "10m", + "10M", + "11P", + "12d", + "12P", + "13m", + "13M", + "14m", + "14M" + ]); + expect( + map(ivl.fromSemitones, [-0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11]) + ).toEqual([ + "1P", + "-2m", + "-2M", + "-3m", + "-3M", + "-4P", + "-5d", + "-5P", + "-6m", + "-6M", + "-7m", + "-7M" + ]); + expect( + map(ivl.fromSemitones, [ + -12, + -13, + -14, + -15, + -16, + -17, + -18, + -19, + -20, + -21, + -22, + -23 + ]) + ).toEqual([ + "-8P", + "-9m", + "-9M", + "-10m", + "-10M", + "-11P", + "-12d", + "-12P", + "-13m", + "-13M", + "-14m", + "-14M" + ]); + }); - test('get interval class', () => { - expect(map(ivl.ic, '1P 2M 3M 4P 5P 6M 7M 8P')).toEqual([ 0, 2, 4, 5, 5, 3, 1, 0 ]) - expect(map(ivl.ic, '1d 2m 3m 4d 5d 6m 7m 8d')).toEqual([ 1, 1, 3, 4, 6, 4, 2, 1 ]) - expect(map(ivl.ic, '8P 9M 10M 11P 12P 13M 14M 15P')).toEqual([ 0, 2, 4, 5, 5, 3, 1, 0 ]) - expect(map(ivl.ic, '-1P -2M -3M -4P -5P -6M -7M -8P')).toEqual([ 0, 2, 4, 5, 5, 3, 1, 0 ]) + test("get interval class", () => { + expect(map(ivl.ic, "1P 2M 3M 4P 5P 6M 7M 8P")).toEqual([ + 0, + 2, + 4, + 5, + 5, + 3, + 1, + 0 + ]); + expect(map(ivl.ic, "1d 2m 3m 4d 5d 6m 7m 8d")).toEqual([ + 1, + 1, + 3, + 4, + 6, + 4, + 2, + 1 + ]); + expect(map(ivl.ic, "8P 9M 10M 11P 12P 13M 14M 15P")).toEqual([ + 0, + 2, + 4, + 5, + 5, + 3, + 1, + 0 + ]); + expect(map(ivl.ic, "-1P -2M -3M -4P -5P -6M -7M -8P")).toEqual([ + 0, + 2, + 4, + 5, + 5, + 3, + 1, + 0 + ]); // from semitones - expect(map(ivl.ic, [0, 2, 4, 5, 7, 9, 11, 12])).toEqual([ 0, 2, 4, 5, 5, 3, 1, 0 ]) - expect(ivl.ic('blah')).toBe(null) - }) + expect(map(ivl.ic, [0, 2, 4, 5, 7, 9, 11, 12])).toEqual([ + 0, + 2, + 4, + 5, + 5, + 3, + 1, + 0 + ]); + expect(ivl.ic("blah")).toBe(null); + }); - test('interval types', () => { - expect(map(ivl.type, '1P 2M 3M 4P 5P 6M 7M')).toEqual([ 'P', 'M', 'M', 'P', 'P', 'M', 'M' ]) - expect(map(ivl.type, '8d 9m 10m 11d 12d 13m 14m')).toEqual([ 'P', 'M', 'M', 'P', 'P', 'M', 'M' ]) - expect(map(ivl.type, '-15A -16A -17A -18A -19A -20A -21A')).toEqual([ 'P', 'M', 'M', 'P', 'P', 'M', 'M' ]) - }) + test("interval types", () => { + expect(map(ivl.type, "1P 2M 3M 4P 5P 6M 7M")).toEqual([ + "P", + "M", + "M", + "P", + "P", + "M", + "M" + ]); + expect(map(ivl.type, "8d 9m 10m 11d 12d 13m 14m")).toEqual([ + "P", + "M", + "M", + "P", + "P", + "M", + "M" + ]); + expect(map(ivl.type, "-15A -16A -17A -18A -19A -20A -21A")).toEqual([ + "P", + "M", + "M", + "P", + "P", + "M", + "M" + ]); + }); - test('simplify intervals', () => { - expect(map(ivl.simplify, '1P 2M 3M 4P 5P 6M 7M')).toEqual([ '1P', '2M', '3M', '4P', '5P', '6M', '7M' ]) - expect(map(ivl.simplify, '8P 9M 10M 11P 12P 13M 14M')).toEqual([ '8P', '2M', '3M', '4P', '5P', '6M', '7M' ]) - expect(map(ivl.simplify, '1d 1P 1A 8d 8P 8A 15d 15P 15A')).toEqual([ '1d', '1P', '1A', '8d', '8P', '8A', '1d', '1P', '1A' ]) - expect(map(ivl.simplify, '-1P -2M -3M -4P -5P -6M -7M')).toEqual([ '-1P', '-2M', '-3M', '-4P', '-5P', '-6M', '-7M' ]) - expect(map(ivl.simplify, '-8P -9M -10M -11P -12P -13M -14M')).toEqual([ '-8P', '-2M', '-3M', '-4P', '-5P', '-6M', '-7M' ]) - }) + test("simplify intervals", () => { + expect(map(ivl.simplify, "1P 2M 3M 4P 5P 6M 7M")).toEqual([ + "1P", + "2M", + "3M", + "4P", + "5P", + "6M", + "7M" + ]); + expect(map(ivl.simplify, "8P 9M 10M 11P 12P 13M 14M")).toEqual([ + "8P", + "2M", + "3M", + "4P", + "5P", + "6M", + "7M" + ]); + expect(map(ivl.simplify, "1d 1P 1A 8d 8P 8A 15d 15P 15A")).toEqual([ + "1d", + "1P", + "1A", + "8d", + "8P", + "8A", + "1d", + "1P", + "1A" + ]); + expect(map(ivl.simplify, "-1P -2M -3M -4P -5P -6M -7M")).toEqual([ + "-1P", + "-2M", + "-3M", + "-4P", + "-5P", + "-6M", + "-7M" + ]); + expect(map(ivl.simplify, "-8P -9M -10M -11P -12P -13M -14M")).toEqual([ + "-8P", + "-2M", + "-3M", + "-4P", + "-5P", + "-6M", + "-7M" + ]); + }); - test('invert intervals', () => { - expect(map(ivl.invert, '1P 2M 3M 4P 5P 6M 7M')).toEqual([ '1P', '7m', '6m', '5P', '4P', '3m', '2m' ]) - expect(map(ivl.invert, '1d 2m 3m 4d 5d 6m 7m')).toEqual([ '1A', '7M', '6M', '5A', '4A', '3M', '2M' ]) - expect(map(ivl.invert, '1A 2A 3A 4A 5A 6A 7A')).toEqual([ '1d', '7d', '6d', '5d', '4d', '3d', '2d' ]) - expect(map(ivl.invert, '-1P -2M -3M -4P -5P -6M -7M')).toEqual([ '-1P', '-7m', '-6m', '-5P', '-4P', '-3m', '-2m' ]) - expect(map(ivl.invert, '8P 9M 10M 11P 12P 13M 14M')).toEqual([ '8P', '14m', '13m', '12P', '11P', '10m', '9m' ]) - }) -}) + test("invert intervals", () => { + expect(map(ivl.invert, "1P 2M 3M 4P 5P 6M 7M")).toEqual([ + "1P", + "7m", + "6m", + "5P", + "4P", + "3m", + "2m" + ]); + expect(map(ivl.invert, "1d 2m 3m 4d 5d 6m 7m")).toEqual([ + "1A", + "7M", + "6M", + "5A", + "4A", + "3M", + "2M" + ]); + expect(map(ivl.invert, "1A 2A 3A 4A 5A 6A 7A")).toEqual([ + "1d", + "7d", + "6d", + "5d", + "4d", + "3d", + "2d" + ]); + expect(map(ivl.invert, "-1P -2M -3M -4P -5P -6M -7M")).toEqual([ + "-1P", + "-7m", + "-6m", + "-5P", + "-4P", + "-3m", + "-2m" + ]); + expect(map(ivl.invert, "8P 9M 10M 11P 12P 13M 14M")).toEqual([ + "8P", + "14m", + "13m", + "12P", + "11P", + "10m", + "9m" + ]); + }); +}); diff --git a/packages/tonal/note/index.js b/packages/tonal/note/index.js index 04f59b5..04c70fa 100644 --- a/packages/tonal/note/index.js +++ b/packages/tonal/note/index.js @@ -30,16 +30,16 @@ * * @module note */ -import { build, parse } from 'note-parser' -import { fifths, asNotePitch, strNote, parseIvl, decode } from 'tonal-pitch' -import { transpose as tr } from 'tonal-transpose' -import { toMidi, note as midiToNote } from 'tonal-midi' -import { toFreq } from 'tonal-freq' +import { build, parse } from "note-parser"; +import { fifths, asNotePitch, strNote, parseIvl, decode } from "tonal-pitch"; +import { transpose as tr } from "tonal-transpose"; +import { toMidi, note as midiToNote } from "tonal-midi"; +import { toFreq } from "tonal-freq"; -var cache = {} -function parseNote (name) { - if (typeof name !== 'string') return null - return cache[name] || (cache[name] = parse(name)) +var cache = {}; +function parseNote(name) { + if (typeof name !== "string") return null; + return cache[name] || (cache[name] = parse(name)); } /** @@ -53,7 +53,7 @@ function parseNote (name) { * note.midi('C4') // => 60 * @see midi.toMidi */ -export var midi = toMidi +export var midi = toMidi; /** * Get the note name of a given midi note number @@ -67,7 +67,7 @@ export var midi = toMidi * note.fromMidi(60) // => 'C4' * @see midi.note */ -export var fromMidi = midiToNote +export var fromMidi = midiToNote; /** * Get the frequency of a note @@ -80,7 +80,7 @@ export var fromMidi = midiToNote * note.freq('A4') // => 440 * @see freq.toFreq */ -export var freq = toFreq +export var freq = toFreq; /** * Return the chroma of a note. The chroma is the numeric equivalent to the @@ -93,9 +93,9 @@ export var freq = toFreq * note.chroma('Cb') // => 11 * ['C', 'D', 'E', 'F'].map(note.chroma) // => [0, 2, 4, 5] */ -export function chroma (n) { - var p = parseNote(n) - return p ? p.chroma : null +export function chroma(n) { + var p = parseNote(n); + return p ? p.chroma : null; } /** @@ -114,9 +114,9 @@ export function chroma (n) { * note.name('cb2') // => 'Cb2' * ['c', 'db3', '2', 'g+', 'gx4'].map(note.name) // => ['C', 'Db3', null, null, 'G##4'] */ -export function name (n) { - var p = asNotePitch(n) - return p ? strNote(p) : null +export function name(n) { + var p = asNotePitch(n); + return p ? strNote(p) : null; } /** @@ -124,9 +124,9 @@ export function name (n) { * An alias for note. Get the name of a note in scientific notation * @function */ -export function note (n) { - console.warn('note.note() is deprecated. Use note.name()') - return name(n) +export function note(n) { + console.warn("note.note() is deprecated. Use note.name()"); + return name(n); } /** @@ -143,12 +143,14 @@ export function note (n) { * note.props('Db3') // => { step: 1, alt: -1, oct: 3 } * note.props('C#') // => { step: 0, alt: 1, oct: undefined } */ -export function props (n) { - console.warn('note.props() is deprecated. Use: note.step(), note.alt() or note.oct()') - var p = asNotePitch(n) - if (!p) return null - var d = decode(p) - return { step: d[0], alt: d[1], oct: d[2] } +export function props(n) { + console.warn( + "note.props() is deprecated. Use: note.step(), note.alt() or note.oct()" + ); + var p = asNotePitch(n); + if (!p) return null; + var d = decode(p); + return { step: d[0], alt: d[1], oct: d[2] }; } /** @@ -167,13 +169,16 @@ export function props (n) { * note.fromProps({ step: 1, alt: -1, oct: 5 }) // => 'Db5' * note.fromProps({ step: 0, alt: 1 }) // => 'C#' */ -export function fromProps (props) { - console.warn('note.fromProps() is deprecated. See npm package note-parser.') - return props ? build(props.step, props.alt, props.oct) : null +export function fromProps(props) { + console.warn("note.fromProps() is deprecated. See npm package note-parser."); + return props ? build(props.step, props.alt, props.oct) : null; } -function getProp (name) { - return function (n) { var p = props(n); return p ? p[name] : null } +function getProp(name) { + return function(n) { + var p = props(n); + return p ? p[name] : null; + }; } /** @@ -188,7 +193,7 @@ function getProp (name) { * note.oct('C') // => undefined * note.oct('blah') // => undefined */ -export var oct = getProp('oct') +export var oct = getProp("oct"); /** * Get the note step: a number equivalent of the note letter. 0 means C and @@ -203,7 +208,7 @@ export var oct = getProp('oct') * // usually what you need is chroma * note.chroma('Cb') // => 6 */ -export var step = getProp('step') +export var step = getProp("step"); /** * @deprecated @@ -213,9 +218,9 @@ export var step = getProp('step') * @param {String|Pitch} note - the note (can be a pitch class) * @return {Integer} the number of fifths to reach that pitch class from 'C' */ -export function pcFifths (note) { - var p = asNotePitch(note) - return p ? fifths(p) : null +export function pcFifths(note) { + var p = asNotePitch(note); + return p ? fifths(p) : null; } /** @@ -230,7 +235,7 @@ export function pcFifths (note) { * note.alt('C#') // => 1 * note.alt('Cb') // => -1 */ -export var alt = getProp('alt') +export var alt = getProp("alt"); /** * Get pitch class of a note. The note can be a string or a pitch array. @@ -242,13 +247,13 @@ export var alt = getProp('alt') * tonal.pc('Db3') // => 'Db' * tonal.map(tonal.pc, 'db3 bb6 fx2') // => [ 'Db', 'Bb', 'F##'] */ -export function pc (n) { - var p = asNotePitch(n) - return p ? strNote([ p[0], [ fifths(p) ] ]) : null +export function pc(n) { + var p = asNotePitch(n); + return p ? strNote([p[0], [fifths(p)]]) : null; } -var ASC = parseIvl('2d') -var DESC = parseIvl('-2d') +var ASC = parseIvl("2d"); +var DESC = parseIvl("-2d"); /** * Get the enharmonics of a note. It returns an array of three elements: the @@ -264,13 +269,13 @@ var DESC = parseIvl('-2d') * note.enharmonics('C#4') // => ['B##3', 'C#4' 'Db4'] * note.enharmonics('Db') // => ['C#', 'Db', 'Ebbb']) */ -export function enharmonics (pitch) { - var notes = [] - notes.push(tr(DESC, pitch)) - if (notes[0] === null) return null - notes.push(pitch) - notes.push(tr(ASC, pitch)) - return notes +export function enharmonics(pitch) { + var notes = []; + notes.push(tr(DESC, pitch)); + if (notes[0] === null) return null; + notes.push(pitch); + notes.push(tr(ASC, pitch)); + return notes; } /** @@ -283,9 +288,9 @@ export function enharmonics (pitch) { * var note = require('tonal-note') * note.simplify('B#3') // => 'C4' */ -export function simplify (pitch) { - return enharmonics(pitch).reduce(function (simple, next) { - if (!simple) return next - return simple.length > next.length ? next : simple - }, null) +export function simplify(pitch) { + return enharmonics(pitch).reduce(function(simple, next) { + if (!simple) return next; + return simple.length > next.length ? next : simple; + }, null); } diff --git a/packages/tonal/note/test/enharmonics.test.js b/packages/tonal/note/test/enharmonics.test.js index 5a6eb8d..23478bc 100644 --- a/packages/tonal/note/test/enharmonics.test.js +++ b/packages/tonal/note/test/enharmonics.test.js @@ -1,34 +1,37 @@ /* global describe test expect */ -var note = require('..') +var note = require(".."); -describe('tonal-enharmonics', () => { - test('enharmonics', () => { - expect(note.enharmonics('C')).toEqual([ 'B#', 'C', 'Dbb' ]) - expect(note.enharmonics('B')).toEqual([ 'A##', 'B', 'Cb' ]) - expect(note.enharmonics('B#')).toEqual([ 'A###', 'B#', 'C' ]) - expect(note.enharmonics('F5')).toEqual([ 'E#5', 'F5', 'Gbb5' ]) - expect(note.enharmonics('E#2')).toEqual([ 'D###2', 'E#2', 'F2' ]) - expect(note.enharmonics('A###6')).toEqual([ 'G#####6', 'A###6', 'B#6' ]) - expect(note.enharmonics('A')).toEqual([ 'G##', 'A', 'Bbb' ]) - expect(note.enharmonics('Ab3')).toEqual([ 'G#3', 'Ab3', 'Bbbb3' ]) - expect(note.enharmonics('Db')).toEqual([ 'C#', 'Db', 'Ebbb' ]) - }) +describe("tonal-enharmonics", () => { + test("enharmonics", () => { + expect(note.enharmonics("C")).toEqual(["B#", "C", "Dbb"]); + expect(note.enharmonics("B")).toEqual(["A##", "B", "Cb"]); + expect(note.enharmonics("B#")).toEqual(["A###", "B#", "C"]); + expect(note.enharmonics("F5")).toEqual(["E#5", "F5", "Gbb5"]); + expect(note.enharmonics("E#2")).toEqual(["D###2", "E#2", "F2"]); + expect(note.enharmonics("A###6")).toEqual(["G#####6", "A###6", "B#6"]); + expect(note.enharmonics("A")).toEqual(["G##", "A", "Bbb"]); + expect(note.enharmonics("Ab3")).toEqual(["G#3", "Ab3", "Bbbb3"]); + expect(note.enharmonics("Db")).toEqual(["C#", "Db", "Ebbb"]); + }); - test('enharmonics - returns empty array if not valid pitch', () => { - expect(note.enharmonics('blah')).toBe(null) - }) + test("enharmonics - returns empty array if not valid pitch", () => { + expect(note.enharmonics("blah")).toBe(null); + }); - test('enharmonics - pitch in array notation', () => { - var C = ['tnlp', [0]] - expect(note.enharmonics(C)).toEqual( - [ [ 'tnlp', [ 12 ] ], [ 'tnlp', [ 0 ] ], [ 'tnlp', [ -12 ] ] ]) - }) + test("enharmonics - pitch in array notation", () => { + var C = ["tnlp", [0]]; + expect(note.enharmonics(C)).toEqual([ + ["tnlp", [12]], + ["tnlp", [0]], + ["tnlp", [-12]] + ]); + }); - test('simplify', () => { - expect(note.simplify('E#2')).toEqual('F2') - expect(note.simplify('B#2')).toEqual('C3') - expect(note.simplify('Cb2')).toEqual('B1') + test("simplify", () => { + expect(note.simplify("E#2")).toEqual("F2"); + expect(note.simplify("B#2")).toEqual("C3"); + expect(note.simplify("Cb2")).toEqual("B1"); // strage case: not a COMPLETE simplification, but I think is enough - expect(note.simplify('A###6')).toEqual('B#6') - }) -}) + expect(note.simplify("A###6")).toEqual("B#6"); + }); +}); diff --git a/packages/tonal/note/test/note.test.js b/packages/tonal/note/test/note.test.js index 67d8e1d..ab1212d 100644 --- a/packages/tonal/note/test/note.test.js +++ b/packages/tonal/note/test/note.test.js @@ -1,45 +1,91 @@ /* global describe test expect */ -var note = require('..') - -describe('tonal-note', () => { - test('step', () => { - expect('c d e f g a b'.split(' ').map(note.step)).toEqual([ 0, 1, 2, 3, 4, 5, 6 ]) - expect('c# d## e### f####'.split(' ').map(note.step)).toEqual([ 0, 1, 2, 3 ]) - }) - - test('oct', () => { - expect('a-2 b-1 c0 d1 e2 f3 g4 a5 b6 c7 d8 c9 d10'.split(' ').map(note.oct)).toEqual([ -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]) - expect('c d e f g'.split(' ').map(note.oct)).toEqual([ undefined, undefined, undefined, undefined, undefined ]) - expect(note.oct('blah') === null).toBeTruthy() - expect(note.oct(['tnlp', [2, 0]])).toBe(1) - }) - - test('midi', () => { - expect('c4 d4 e4 f4 g4 a4 b4 c4'.split(' ').map(note.midi)).toEqual([ 60, 62, 64, 65, 67, 69, 71, 60 ]) - }) - - test.only('toFreq', () => { - expect(note.freq('A4')).toBe(440) - }) - - test('chroma', () => { - expect('Cb C Db D Eb E Fb F Gb G Ab A Bb B'.split(' ').map(note.chroma)).toEqual([ 11, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11 ]) - expect('C C# D D# E E# F F# G G# A A# B B#'.split(' ').map(note.chroma)).toEqual([ 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 0 ]) - }) - - test('name', () => { - expect('c fx dbb bbb c##-1 fbb6'.split(' ').map(note.name)).toEqual([ 'C', 'F##', 'Dbb', 'Bbb', 'C##-1', 'Fbb6' ]) - }) - - test('pc', () => { - expect('a b0 d2 e# fb3 g###4 bbbb5 h j'.split(' ').map(note.pc)).toEqual([ 'A', 'B', 'D', 'E#', 'Fb', 'G###', 'Bbbb', null, null ]) - }) - - test('fromProps', () => { - expect(note.fromProps({ step: 1, alt: -1, oct: 2 })).toBe('Db2') - expect(note.fromProps({ step: 4, alt: 1 })).toBe('G#') - expect(note.fromProps({ step: 1 })).toBe('D') - expect(note.fromProps({})).toBe(null) - expect(note.fromProps()).toBe(null) - }) -}) +var note = require(".."); + +describe("tonal-note", () => { + test("step", () => { + expect("c d e f g a b".split(" ").map(note.step)).toEqual([ + 0, + 1, + 2, + 3, + 4, + 5, + 6 + ]); + expect("c# d## e### f####".split(" ").map(note.step)).toEqual([0, 1, 2, 3]); + }); + + test("oct", () => { + expect( + "a-2 b-1 c0 d1 e2 f3 g4 a5 b6 c7 d8 c9 d10".split(" ").map(note.oct) + ).toEqual([-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + expect("c d e f g".split(" ").map(note.oct)).toEqual([ + undefined, + undefined, + undefined, + undefined, + undefined + ]); + expect(note.oct("blah") === null).toBeTruthy(); + expect(note.oct(["tnlp", [2, 0]])).toBe(1); + }); + + test("midi", () => { + expect("c4 d4 e4 f4 g4 a4 b4 c4".split(" ").map(note.midi)).toEqual([ + 60, + 62, + 64, + 65, + 67, + 69, + 71, + 60 + ]); + }); + + test.only("toFreq", () => { + expect(note.freq("A4")).toBe(440); + }); + + test("chroma", () => { + expect( + "Cb C Db D Eb E Fb F Gb G Ab A Bb B".split(" ").map(note.chroma) + ).toEqual([11, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11]); + expect( + "C C# D D# E E# F F# G G# A A# B B#".split(" ").map(note.chroma) + ).toEqual([0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 0]); + }); + + test("name", () => { + expect("c fx dbb bbb c##-1 fbb6".split(" ").map(note.name)).toEqual([ + "C", + "F##", + "Dbb", + "Bbb", + "C##-1", + "Fbb6" + ]); + }); + + test("pc", () => { + expect("a b0 d2 e# fb3 g###4 bbbb5 h j".split(" ").map(note.pc)).toEqual([ + "A", + "B", + "D", + "E#", + "Fb", + "G###", + "Bbbb", + null, + null + ]); + }); + + test("fromProps", () => { + expect(note.fromProps({ step: 1, alt: -1, oct: 2 })).toBe("Db2"); + expect(note.fromProps({ step: 4, alt: 1 })).toBe("G#"); + expect(note.fromProps({ step: 1 })).toBe("D"); + expect(note.fromProps({})).toBe(null); + expect(note.fromProps()).toBe(null); + }); +}); diff --git a/packages/tonal/scale/index.js b/packages/tonal/scale/index.js index 94544f5..f9a33c4 100644 --- a/packages/tonal/scale/index.js +++ b/packages/tonal/scale/index.js @@ -12,13 +12,15 @@ * scale.detect('f5 d2 c5 b5 a2 e4 g') // => [ 'C major', 'D dorian', 'E phrygian', 'F lydian', 'G mixolydian', 'A aeolian', 'B locrian']) * @module scale */ -import { dictionary, detector } from 'tonal-dictionary' -import { map, compact } from 'tonal-array' -import { pc, name as note } from 'tonal-note' -import { harmonize } from 'tonal-harmonizer' -import DATA from './scales.json' +import { dictionary, detector } from "tonal-dictionary"; +import { map, compact } from "tonal-array"; +import { pc, name as note } from "tonal-note"; +import { harmonize } from "tonal-harmonizer"; +import DATA from "./scales.json"; -var dict = dictionary(DATA, function (str) { return str.split(' ') }) +var dict = dictionary(DATA, function(str) { + return str.split(" "); +}); /** * Transpose the given scale notes, intervals or name to a given tonic. @@ -38,10 +40,13 @@ var dict = dictionary(DATA, function (str) { return str.split(' ') }) * var major = scale.get('major') * major('Db3') // => [ 'Db3', 'Eb3', 'F3', 'Gb3', 'Ab3', 'Bb3', 'C4' ] */ -export function get (type, tonic) { - if (arguments.length === 1) return function (t) { return get(type, t) } - var ivls = dict.get(type) - return ivls ? harmonize(ivls, tonic) : null +export function get(type, tonic) { + if (arguments.length === 1) + return function(t) { + return get(type, t); + }; + var ivls = dict.get(type); + return ivls ? harmonize(ivls, tonic) : null; } /** @@ -55,7 +60,7 @@ export function get (type, tonic) { * var scale = require('tonal-scale') * scale.names() // => ['maj7', ...] */ -export var names = dict.keys +export var names = dict.keys; /** * Get the notes (pitch classes) of a scale. It accepts either a scale name @@ -73,14 +78,19 @@ export var names = dict.keys * scale.notes('Ab bebop') // => [ 'Ab', 'Bb', 'C', 'Db', 'Eb', 'F', 'Gb', 'G' ] * scale.notes('C4 D6 E2 c7 a2 b5 g2 g4 f') // => ['C', 'D', 'E', 'F', 'G', 'A', 'B'] */ -export function notes (name) { - var scale = parse(name) - var notes = scale.tonic ? get(scale.type, pc(scale.tonic)) : null - return notes || compact(map(pc, name).map(function (n, i, arr) { - // check for duplicates - // TODO: sort but preserving the root - return arr.indexOf(n) < i ? null : n - })) +export function notes(name) { + var scale = parse(name); + var notes = scale.tonic ? get(scale.type, pc(scale.tonic)) : null; + return ( + notes || + compact( + map(pc, name).map(function(n, i, arr) { + // check for duplicates + // TODO: sort but preserving the root + return arr.indexOf(n) < i ? null : n; + }) + ) + ); } /** @@ -95,9 +105,9 @@ export function notes (name) { * @example * scale.intervals('C major') */ -export function intervals (name) { - var scale = parse(name) - return get(scale.type, false) || [] +export function intervals(name) { + var scale = parse(name); + return get(scale.type, false) || []; } /** @@ -109,8 +119,8 @@ export function intervals (name) { * scale.intervals('major') // => [ '1P', '2M', '3M', '4P', '5P', '6M', '7M' ]) * scale.intervals('mixophrygian') // => null */ -export function isKnowScale (name) { - return intervals(name).length > 0 +export function isKnowScale(name) { + return intervals(name).length > 0; } /** @@ -127,12 +137,12 @@ export function isKnowScale (name) { * scale.parse('C mixoblydean') // => { tonic: 'C', type: 'mixoblydean' } * scale.parse('anything is valid') // => { tonic: false, type: 'anything is valid'} */ -export function parse (str) { - if (typeof str !== 'string') return null - var i = str.indexOf(' ') - var tonic = note(str.substring(0, i)) || false - var type = tonic ? str.substring(i + 1) : str - return { tonic: tonic, type: type } +export function parse(str) { + if (typeof str !== "string") return null; + var i = str.indexOf(" "); + var tonic = note(str.substring(0, i)) || false; + var type = tonic ? str.substring(i + 1) : str; + return { tonic: tonic, type: type }; } /** @@ -146,4 +156,4 @@ export function parse (str) { * scale.detect('b g f# d') // => [ 'GMaj7' ] * scale.detect('e c a g') // => [ 'CM6', 'Am7' ] */ -export var detect = detector(dict, ' ') +export var detect = detector(dict, " "); diff --git a/packages/tonal/scale/test/scale.test.js b/packages/tonal/scale/test/scale.test.js index e4d6fbd..6cd3f5c 100644 --- a/packages/tonal/scale/test/scale.test.js +++ b/packages/tonal/scale/test/scale.test.js @@ -1,62 +1,137 @@ /* global describe test expect */ -var scale = require('..') - -describe('tonal-scale', () => { - test('parse', () => { - expect(scale.parse('cb3 major')).toEqual({ tonic: 'Cb3', type: 'major' }) - expect(scale.parse('melodic minor')).toEqual({ tonic: false, type: 'melodic minor' }) - expect(scale.parse()).toBe(null) - }) - - test('isKnowScale', () => { - expect(scale.isKnowScale('major')).toBe(true) - expect(scale.isKnowScale('Maj7')).toBe(false) - }) - - test('intervals', () => { - expect(scale.intervals('C major')).toEqual([ '1P', '2M', '3M', '4P', '5P', '6M', '7M' ]) - expect(scale.intervals('major')).toEqual([ '1P', '2M', '3M', '4P', '5P', '6M', '7M' ]) - expect(scale.intervals('blah')).toEqual([]) - }) - - test('notes', () => { - expect(scale.notes('C major')).toEqual([ 'C', 'D', 'E', 'F', 'G', 'A', 'B' ]) - expect(scale.notes('C4 major')).toEqual(scale.notes('C major')) - expect(scale.notes('Eb bebop')).toEqual([ 'Eb', 'F', 'G', 'Ab', 'Bb', 'C', 'Db', 'D' ]) - expect(scale.notes('d4 e5 g3 c6 d5')).toEqual(['D', 'E', 'G', 'C']) - expect(scale.notes('Cmaj7')).toEqual([]) - expect(scale.notes('blah')).toEqual([]) - }) - - test('get', () => { - expect(scale.get('major', 'C')).toEqual([ 'C', 'D', 'E', 'F', 'G', 'A', 'B' ]) - expect(scale.get('major', 'C2')).toEqual([ 'C2', 'D2', 'E2', 'F2', 'G2', 'A2', 'B2' ]) +var scale = require(".."); + +describe("tonal-scale", () => { + test("parse", () => { + expect(scale.parse("cb3 major")).toEqual({ tonic: "Cb3", type: "major" }); + expect(scale.parse("melodic minor")).toEqual({ + tonic: false, + type: "melodic minor" + }); + expect(scale.parse()).toBe(null); + }); + + test("isKnowScale", () => { + expect(scale.isKnowScale("major")).toBe(true); + expect(scale.isKnowScale("Maj7")).toBe(false); + }); + + test("intervals", () => { + expect(scale.intervals("C major")).toEqual([ + "1P", + "2M", + "3M", + "4P", + "5P", + "6M", + "7M" + ]); + expect(scale.intervals("major")).toEqual([ + "1P", + "2M", + "3M", + "4P", + "5P", + "6M", + "7M" + ]); + expect(scale.intervals("blah")).toEqual([]); + }); + + test("notes", () => { + expect(scale.notes("C major")).toEqual(["C", "D", "E", "F", "G", "A", "B"]); + expect(scale.notes("C4 major")).toEqual(scale.notes("C major")); + expect(scale.notes("Eb bebop")).toEqual([ + "Eb", + "F", + "G", + "Ab", + "Bb", + "C", + "Db", + "D" + ]); + expect(scale.notes("d4 e5 g3 c6 d5")).toEqual(["D", "E", "G", "C"]); + expect(scale.notes("Cmaj7")).toEqual([]); + expect(scale.notes("blah")).toEqual([]); + }); + + test("get", () => { + expect(scale.get("major", "C")).toEqual([ + "C", + "D", + "E", + "F", + "G", + "A", + "B" + ]); + expect(scale.get("major", "C2")).toEqual([ + "C2", + "D2", + "E2", + "F2", + "G2", + "A2", + "B2" + ]); // alias - expect(scale.get('ionian', 'C')).toEqual([ 'C', 'D', 'E', 'F', 'G', 'A', 'B' ]) + expect(scale.get("ionian", "C")).toEqual([ + "C", + "D", + "E", + "F", + "G", + "A", + "B" + ]); // intervals - expect(scale.get('major', false)).toEqual([ '1P', '2M', '3M', '4P', '5P', '6M', '7M' ]) + expect(scale.get("major", false)).toEqual([ + "1P", + "2M", + "3M", + "4P", + "5P", + "6M", + "7M" + ]); // partially applied - expect(scale.get('major')('Db3')).toEqual([ 'Db3', 'Eb3', 'F3', 'Gb3', 'Ab3', 'Bb3', 'C4' ]) + expect(scale.get("major")("Db3")).toEqual([ + "Db3", + "Eb3", + "F3", + "Gb3", + "Ab3", + "Bb3", + "C4" + ]); // not found - expect(scale.get('no-scale', 'D')).toEqual(null) - expect(scale.get('major', 'blah')).toEqual([]) - }) - - test('names', () => { - expect(scale.names().length > 0).toBeTruthy() - expect(scale.names(true).length > scale.names().length).toBeTruthy() - }) - - test('names with filter', () => { - expect(scale.names(true, function (name, intervals) { - return intervals.length === 9 - })).toEqual([ 'composite blues' ]) - }) - - test('detect', () => { - expect(scale.detect('f3 a c5 e2 d g2 b6')).toEqual([ - 'C major', 'D dorian', 'E phrygian', 'F lydian', 'G mixolydian', - 'A aeolian', 'B locrian' - ]) - }) -}) + expect(scale.get("no-scale", "D")).toEqual(null); + expect(scale.get("major", "blah")).toEqual([]); + }); + + test("names", () => { + expect(scale.names().length > 0).toBeTruthy(); + expect(scale.names(true).length > scale.names().length).toBeTruthy(); + }); + + test("names with filter", () => { + expect( + scale.names(true, function(name, intervals) { + return intervals.length === 9; + }) + ).toEqual(["composite blues"]); + }); + + test("detect", () => { + expect(scale.detect("f3 a c5 e2 d g2 b6")).toEqual([ + "C major", + "D dorian", + "E phrygian", + "F lydian", + "G mixolydian", + "A aeolian", + "B locrian" + ]); + }); +}); diff --git a/rollup.config.dist.js b/rollup.config.dist.js index cd586c3..c4c1c58 100644 --- a/rollup.config.dist.js +++ b/rollup.config.dist.js @@ -1,12 +1,15 @@ -import resolve from 'rollup-plugin-node-resolve' -import json from 'rollup-plugin-json' -import uglify from 'rollup-plugin-uglify' +import resolve from "rollup-plugin-node-resolve"; +import json from "rollup-plugin-json"; +import uglify from "rollup-plugin-uglify"; export default { - entry: './packages/core/tonal/index.js', - format: 'umd', - dest: './dist/tonal.min.js', - moduleName: 'Tonal', + input: "./packages/core/tonal/index.js", + output: { + format: "umd", + file: "./dist/tonal.min.js", + name: "Tonal", + sourcemap: true + }, preferConst: false, plugins: [ json({ @@ -19,4 +22,4 @@ export default { }), uglify() ] -} +}; diff --git a/rollup.config.js b/rollup.config.js index 2224f09..80afef2 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,11 +1,14 @@ -import json from 'rollup-plugin-json' +import json from "rollup-plugin-json"; export default { - format: 'cjs', + output: { + file: "build/index.js", + format: "cjs" + }, preferConst: false, plugins: [ json({ preferConst: false }) ] -} +}; diff --git a/yarn.lock b/yarn.lock index 6784e40..9061593 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19,21 +19,11 @@ acorn-globals@^3.1.0: dependencies: acorn "^4.0.4" -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - dependencies: - acorn "^3.0.4" - -acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - -acorn@^4.0.1, acorn@^4.0.4: +acorn@^4.0.4: version "4.0.13" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" -acorn@^5.0.1: +acorn@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.1.tgz#53fe161111f912ab999ee887a90a0bc52822fd75" @@ -41,11 +31,7 @@ add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" -ajv-keywords@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" - -ajv@^4.7.0, ajv@^4.9.1: +ajv@^4.9.1: version "4.11.8" resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" dependencies: @@ -64,7 +50,7 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" -ansi-escapes@^1.1.0, ansi-escapes@^1.4.0: +ansi-escapes@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" @@ -90,6 +76,12 @@ ansi-styles@^3.0.0: dependencies: color-convert "^1.0.0" +ansi-styles@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" + dependencies: + color-convert "^1.9.0" + anymatch@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" @@ -194,7 +186,7 @@ aws4@^1.2.1: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" -babel-code-frame@^6.16.0, babel-code-frame@^6.22.0: +babel-code-frame@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" dependencies: @@ -383,7 +375,7 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" -builtin-modules@^1.0.0, builtin-modules@^1.1.0, builtin-modules@^1.1.1: +builtin-modules@^1.0.0, builtin-modules@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -391,16 +383,6 @@ byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - dependencies: - callsites "^0.2.0" - -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" - callsites@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" @@ -439,7 +421,7 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.1.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -449,20 +431,18 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.0.0, chalk@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" + dependencies: + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" + ci-info@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.0.0.tgz#dc5285f2b4e251821683681c381c3388f46ec534" -circular-json@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" - -cli-cursor@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - dependencies: - restore-cursor "^1.0.1" - cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -508,7 +488,7 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" -color-convert@^1.0.0: +color-convert@^1.0.0, color-convert@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" dependencies: @@ -552,7 +532,7 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.4.10, concat-stream@^1.5.2: +concat-stream@^1.4.10: version "1.6.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" dependencies: @@ -564,55 +544,50 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" -contains-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" - content-type-parser@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.1.tgz#c3e56988c53c65127fb46d4032a3a900246fdc94" -conventional-changelog-angular@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-1.3.4.tgz#7d7cdfbd358948312904d02229a61fd6075cf455" +conventional-changelog-angular@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-1.5.0.tgz#50b2d45008448455fdf67e06ea01972fbd08182a" dependencies: compare-func "^1.3.1" - github-url-from-git "^1.4.0" q "^1.4.1" -conventional-changelog-atom@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-atom/-/conventional-changelog-atom-0.1.0.tgz#67a47c66a42b2f8909ef1587c9989ae1de730b92" +conventional-changelog-atom@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/conventional-changelog-atom/-/conventional-changelog-atom-0.1.1.tgz#d40a9b297961b53c745e5d1718fd1a3379f6a92f" dependencies: q "^1.4.1" -conventional-changelog-cli@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/conventional-changelog-cli/-/conventional-changelog-cli-1.3.1.tgz#1cd5a9dbae25ffb5ffe67afef1e136eaceefd2d5" +conventional-changelog-cli@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/conventional-changelog-cli/-/conventional-changelog-cli-1.3.3.tgz#ca38f229a27ec14036021b1786a48f5b8d48d7ff" dependencies: add-stream "^1.0.0" - conventional-changelog "^1.1.3" + conventional-changelog "^1.1.5" lodash "^4.1.0" meow "^3.7.0" tempfile "^1.1.1" -conventional-changelog-codemirror@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-codemirror/-/conventional-changelog-codemirror-0.1.0.tgz#7577a591dbf9b538e7a150a7ee62f65a2872b334" +conventional-changelog-codemirror@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-codemirror/-/conventional-changelog-codemirror-0.2.0.tgz#3cc925955f3b14402827b15168049821972d9459" dependencies: q "^1.4.1" -conventional-changelog-core@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-1.9.0.tgz#de5dfbc091847656508d4a389e35c9a1bc49e7f4" +conventional-changelog-core@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-1.9.1.tgz#ddf767c405850dfc8df31726c80fa1a6a10bdc7b" dependencies: - conventional-changelog-writer "^1.1.0" - conventional-commits-parser "^1.0.0" + conventional-changelog-writer "^2.0.1" + conventional-commits-parser "^2.0.0" dateformat "^1.0.12" get-pkg-repo "^1.0.0" git-raw-commits "^1.2.0" git-remote-origin-url "^2.0.0" - git-semver-tags "^1.2.0" + git-semver-tags "^1.2.1" lodash "^4.0.0" normalize-package-data "^2.3.5" q "^1.4.1" @@ -620,21 +595,21 @@ conventional-changelog-core@^1.9.0: read-pkg-up "^1.0.1" through2 "^2.0.0" -conventional-changelog-ember@^0.2.6: - version "0.2.6" - resolved "https://registry.yarnpkg.com/conventional-changelog-ember/-/conventional-changelog-ember-0.2.6.tgz#8b7355419f5127493c4c562473ab2fc792f1c2b6" +conventional-changelog-ember@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/conventional-changelog-ember/-/conventional-changelog-ember-0.2.7.tgz#c6aff35976284e7222649f81c62bd96ff3217bd2" dependencies: q "^1.4.1" -conventional-changelog-eslint@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-eslint/-/conventional-changelog-eslint-0.1.0.tgz#a52411e999e0501ce500b856b0a643d0330907e2" +conventional-changelog-eslint@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-eslint/-/conventional-changelog-eslint-0.2.0.tgz#b4b9b5dc09417844d87c7bcfb16bdcc686c4b1c1" dependencies: q "^1.4.1" -conventional-changelog-express@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-express/-/conventional-changelog-express-0.1.0.tgz#55c6c841c811962036c037bdbd964a54ae310fce" +conventional-changelog-express@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-express/-/conventional-changelog-express-0.2.0.tgz#8d666ad41b10ebf964a4602062ddd2e00deb518d" dependencies: q "^1.4.1" @@ -650,16 +625,16 @@ conventional-changelog-jscs@^0.1.0: dependencies: q "^1.4.1" -conventional-changelog-jshint@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-jshint/-/conventional-changelog-jshint-0.1.0.tgz#00cab8e9a3317487abd94c4d84671342918d2a07" +conventional-changelog-jshint@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-jshint/-/conventional-changelog-jshint-0.2.0.tgz#63ad7aec66cd1ae559bafe80348c4657a6eb1872" dependencies: compare-func "^1.3.1" q "^1.4.1" -conventional-changelog-writer@^1.1.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-1.4.1.tgz#3f4cb4d003ebb56989d30d345893b52a43639c8e" +conventional-changelog-writer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-2.0.1.tgz#47c10d0faba526b78d194389d1e931d09ee62372" dependencies: compare-func "^1.3.1" conventional-commits-filter "^1.0.0" @@ -672,20 +647,20 @@ conventional-changelog-writer@^1.1.0: split "^1.0.0" through2 "^2.0.0" -conventional-changelog@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-1.1.4.tgz#108bc750c2a317e200e2f9b413caaa1f8c7efa3b" - dependencies: - conventional-changelog-angular "^1.3.4" - conventional-changelog-atom "^0.1.0" - conventional-changelog-codemirror "^0.1.0" - conventional-changelog-core "^1.9.0" - conventional-changelog-ember "^0.2.6" - conventional-changelog-eslint "^0.1.0" - conventional-changelog-express "^0.1.0" +conventional-changelog@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-1.1.5.tgz#4c46fb64b2986cab19888d8c4b87ca7c0e431bfd" + dependencies: + conventional-changelog-angular "^1.5.0" + conventional-changelog-atom "^0.1.1" + conventional-changelog-codemirror "^0.2.0" + conventional-changelog-core "^1.9.1" + conventional-changelog-ember "^0.2.7" + conventional-changelog-eslint "^0.2.0" + conventional-changelog-express "^0.2.0" conventional-changelog-jquery "^0.1.0" conventional-changelog-jscs "^0.1.0" - conventional-changelog-jshint "^0.1.0" + conventional-changelog-jshint "^0.2.0" conventional-commits-filter@^1.0.0: version "1.0.0" @@ -694,9 +669,9 @@ conventional-commits-filter@^1.0.0: is-subset "^0.1.1" modify-values "^1.0.0" -conventional-commits-parser@^1.0.0, conventional-commits-parser@^1.0.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-1.3.0.tgz#e327b53194e1a7ad5dc63479ee9099a52b024865" +conventional-commits-parser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-2.0.0.tgz#71d01910cb0a99aeb20c144e50f81f4df3178447" dependencies: JSONStream "^1.0.4" is-text-path "^1.0.0" @@ -706,15 +681,15 @@ conventional-commits-parser@^1.0.0, conventional-commits-parser@^1.0.1: through2 "^2.0.0" trim-off-newlines "^1.0.0" -conventional-recommended-bump@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-1.0.0.tgz#6d303a27837ae938b7c68c8ddeed34559b4b0789" +conventional-recommended-bump@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-1.0.1.tgz#56b8ae553a8a1152fa069e767599e1f6948bd36c" dependencies: concat-stream "^1.4.10" conventional-commits-filter "^1.0.0" - conventional-commits-parser "^1.0.1" + conventional-commits-parser "^2.0.0" git-raw-commits "^1.2.0" - git-semver-tags "^1.2.0" + git-semver-tags "^1.2.1" meow "^3.3.0" object-assign "^4.0.1" @@ -767,12 +742,6 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" -d@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" - dependencies: - es5-ext "^0.10.9" - dargs@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17" @@ -792,7 +761,7 @@ dateformat@^1.0.11, dateformat@^1.0.12: get-stdin "^4.0.1" meow "^3.3.0" -debug@^2.1.1, debug@^2.2.0, debug@^2.6.3, debug@^2.6.8: +debug@^2.1.1, debug@^2.2.0, debug@^2.6.3: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: @@ -822,18 +791,6 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -del@^2.0.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" - dependencies: - globby "^5.0.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - rimraf "^2.2.8" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -856,20 +813,6 @@ diff@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.0.tgz#056695150d7aa93237ca7e378ac3b1682b7963b9" -doctrine@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - -doctrine@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - dot-prop@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177" @@ -892,64 +835,12 @@ errno@^0.1.4: dependencies: prr "~0.0.0" -error-ex@^1.2.0: +error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" dependencies: is-arrayish "^0.2.1" -es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.23" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.23.tgz#7578b51be974207a5487821b56538c224e4e7b38" - dependencies: - es6-iterator "2" - es6-symbol "~3.1" - -es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-symbol "^3.1" - -es6-map@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-set "~0.1.5" - es6-symbol "~3.1.1" - event-emitter "~0.3.5" - -es6-set@~0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-symbol "3.1.1" - event-emitter "~0.3.5" - -es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" - dependencies: - d "1" - es5-ext "~0.10.14" - -es6-weak-map@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -965,113 +856,6 @@ escodegen@^1.6.1: optionalDependencies: source-map "~0.2.0" -escope@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" - dependencies: - es6-map "^0.1.3" - es6-weak-map "^2.0.1" - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-config-standard@^10.2.1: - version "10.2.1" - resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz#c061e4d066f379dc17cd562c64e819b4dd454591" - -eslint-import-resolver-node@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz#4422574cde66a9a7b099938ee4d508a199e0e3cc" - dependencies: - debug "^2.6.8" - resolve "^1.2.0" - -eslint-module-utils@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449" - dependencies: - debug "^2.6.8" - pkg-dir "^1.0.0" - -eslint-plugin-import@^2.2.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.7.0.tgz#21de33380b9efb55f5ef6d2e210ec0e07e7fa69f" - dependencies: - builtin-modules "^1.1.1" - contains-path "^0.1.0" - debug "^2.6.8" - doctrine "1.5.0" - eslint-import-resolver-node "^0.3.1" - eslint-module-utils "^2.1.1" - has "^1.0.1" - lodash.cond "^4.3.0" - minimatch "^3.0.3" - read-pkg-up "^2.0.0" - -eslint-plugin-node@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-4.2.2.tgz#82959ca9aed79fcbd28bb1b188d05cac04fb3363" - dependencies: - ignore "^3.0.11" - minimatch "^3.0.2" - object-assign "^4.0.1" - resolve "^1.1.7" - semver "5.3.0" - -eslint-plugin-promise@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.5.0.tgz#78fbb6ffe047201627569e85a6c5373af2a68fca" - -eslint-plugin-standard@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-3.0.1.tgz#34d0c915b45edc6f010393c7eef3823b08565cf2" - -eslint@^3.14.1: - version "3.19.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" - dependencies: - babel-code-frame "^6.16.0" - chalk "^1.1.3" - concat-stream "^1.5.2" - debug "^2.1.1" - doctrine "^2.0.0" - escope "^3.6.0" - espree "^3.4.0" - esquery "^1.0.0" - estraverse "^4.2.0" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - glob "^7.0.3" - globals "^9.14.0" - ignore "^3.2.0" - imurmurhash "^0.1.4" - inquirer "^0.12.0" - is-my-json-valid "^2.10.0" - is-resolvable "^1.0.0" - js-yaml "^3.5.1" - json-stable-stringify "^1.0.0" - levn "^0.3.0" - lodash "^4.0.0" - mkdirp "^0.5.0" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.1" - pluralize "^1.2.1" - progress "^1.1.8" - require-uncached "^1.0.2" - shelljs "^0.7.5" - strip-bom "^3.0.0" - strip-json-comments "~2.0.1" - table "^3.7.8" - text-table "~0.2.0" - user-home "^2.0.0" - -espree@^3.4.0: - version "3.4.3" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.3.tgz#2910b5ccd49ce893c2ffffaab4fd8b3a31b82374" - dependencies: - acorn "^5.0.1" - acorn-jsx "^3.0.0" - esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -1080,42 +864,22 @@ esprima@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" -esquery@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa" - dependencies: - estraverse "^4.0.0" - -esrecurse@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" - dependencies: - estraverse "^4.1.0" - object-assign "^4.0.1" - estraverse@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - estree-walker@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.3.1.tgz#e6b1a51cf7292524e7237c312e5fe6660c1ce1aa" +estree-walker@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.0.tgz#aae3b57c42deb8010e349c892462f0e71c5dd1aa" + esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" -event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - dependencies: - d "1" - es5-ext "~0.10.14" - exec-sh@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.0.tgz#14f75de3f20d286ef933099b2ce50a90359cef10" @@ -1134,9 +898,9 @@ execa@^0.5.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.6.3.tgz#57b69a594f081759c69e5370f0d17b9cb11658fe" +execa@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" dependencies: cross-spawn "^5.0.1" get-stream "^3.0.0" @@ -1146,10 +910,6 @@ execa@^0.6.3: signal-exit "^3.0.0" strip-eof "^1.0.0" -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" @@ -1200,26 +960,12 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" -figures@^1.3.5: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" dependencies: escape-string-regexp "^1.0.5" -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" @@ -1254,15 +1000,6 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" -flat-cache@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" - dependencies: - circular-json "^0.3.1" - del "^2.0.2" - graceful-fs "^4.1.2" - write "^0.2.1" - for-in@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -1285,9 +1022,9 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -fs-extra@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291" +fs-extra@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.1.tgz#7fc0c6c8957f983f57f306a24e5b9ddd8d0dd880" dependencies: graceful-fs "^4.1.2" jsonfile "^3.0.0" @@ -1297,10 +1034,6 @@ fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" -function-bind@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" - gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -1314,16 +1047,6 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -generate-function@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" - -generate-object-property@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" - dependencies: - is-property "^1.0.0" - get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" @@ -1338,9 +1061,9 @@ get-pkg-repo@^1.0.0: parse-github-repo-url "^1.3.0" through2 "^2.0.0" -get-port@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.1.0.tgz#ef01b18a84ca6486970ff99e54446141a73ffd3e" +get-port@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" get-stdin@^4.0.1: version "4.0.1" @@ -1380,9 +1103,9 @@ git-remote-origin-url@^2.0.0: gitconfiglocal "^1.0.0" pify "^2.3.0" -git-semver-tags@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-1.2.0.tgz#b31fd02c8ab578bd6c9b5cacca5e1c64c1177ac1" +git-semver-tags@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-1.2.1.tgz#6ccd2a52e735b736748dc762444fcd9588e27490" dependencies: meow "^3.3.0" semver "^5.0.1" @@ -1393,10 +1116,6 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" -github-url-from-git@^1.4.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/github-url-from-git/-/github-url-from-git-1.5.0.tgz#f985fedcc0a9aa579dc88d7aff068d55cc6251a0" - glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" @@ -1410,7 +1129,14 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -1421,21 +1147,10 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^9.0.0, globals@^9.14.0: +globals@^9.0.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" -globby@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" - dependencies: - array-union "^1.0.1" - arrify "^1.0.0" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -1489,16 +1204,14 @@ has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" -has@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" - dependencies: - function-bind "^1.0.2" - hawk@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" @@ -1545,10 +1258,6 @@ iconv-lite@^0.4.17: version "0.4.18" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2" -ignore@^3.0.11, ignore@^3.2.0: - version "3.3.3" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d" - imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -1574,30 +1283,12 @@ ini@^1.3.2: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" -inquirer@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" - dependencies: - ansi-escapes "^1.1.0" - ansi-regex "^2.0.0" - chalk "^1.0.0" - cli-cursor "^1.0.1" - cli-width "^2.0.0" - figures "^1.3.5" - lodash "^4.3.0" - readline2 "^1.0.1" - run-async "^0.1.0" - rx-lite "^3.1.2" - string-width "^1.0.1" - strip-ansi "^3.0.0" - through "^2.3.6" - -inquirer@^3.0.6: - version "3.1.1" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.1.1.tgz#87621c4fba4072f48a8dd71c9f9df6f100b2d534" +inquirer@^3.2.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.2.3.tgz#1c7b1731cf77b934ec47d22c9ac5aa8fe7fbe095" dependencies: ansi-escapes "^2.0.0" - chalk "^1.0.0" + chalk "^2.0.0" cli-cursor "^2.1.0" cli-width "^2.0.0" external-editor "^2.0.4" @@ -1607,14 +1298,10 @@ inquirer@^3.0.6: run-async "^2.2.0" rx-lite "^4.0.8" rx-lite-aggregates "^4.0.8" - string-width "^2.0.0" - strip-ansi "^3.0.0" + string-width "^2.1.0" + strip-ansi "^4.0.0" through "^2.3.6" -interpret@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90" - invariant@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" @@ -1663,6 +1350,10 @@ is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" +is-extglob@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + is-finite@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" @@ -1685,19 +1376,16 @@ is-glob@^2.0.0, is-glob@^2.0.1: dependencies: is-extglob "^1.0.0" +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" -is-my-json-valid@^2.10.0: - version "2.16.0" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" - dependencies: - generate-function "^2.0.0" - generate-object-property "^1.1.0" - jsonpointer "^4.0.0" - xtend "^4.0.0" - is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" @@ -1714,22 +1402,6 @@ is-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" -is-path-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" - -is-path-in-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" - dependencies: - is-path-inside "^1.0.0" - -is-path-inside@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" - dependencies: - path-is-inside "^1.0.1" - is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -1746,16 +1418,6 @@ is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" -is-property@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - -is-resolvable@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" - dependencies: - tryit "^1.0.1" - is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -1778,7 +1440,7 @@ is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -2074,7 +1736,7 @@ js-tokens@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" -js-yaml@^3.5.1, js-yaml@^3.7.0: +js-yaml@^3.7.0: version "3.9.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.9.0.tgz#4ffbbf25c2ac963b8299dc74da7e3740de1c18ce" dependencies: @@ -2121,7 +1783,7 @@ json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" -json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: +json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" dependencies: @@ -2149,10 +1811,6 @@ jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" -jsonpointer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" - jsprim@^1.2.2: version "1.4.0" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" @@ -2184,51 +1842,52 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -lerna@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-2.0.0.tgz#49a72fe70e06aebfd7ea23efb2ab41abe60ebeea" +lerna@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-2.1.2.tgz#b07eb7a4d7dd7d44a105262fef49b2229301c577" dependencies: async "^1.5.0" - chalk "^1.1.1" + chalk "^2.1.0" cmd-shim "^2.0.2" columnify "^1.5.4" command-join "^2.0.0" - conventional-changelog-cli "^1.3.1" - conventional-recommended-bump "^1.0.0" + conventional-changelog-cli "^1.3.2" + conventional-recommended-bump "^1.0.1" dedent "^0.7.0" - execa "^0.6.3" + execa "^0.8.0" find-up "^2.1.0" - fs-extra "^3.0.1" - get-port "^3.1.0" + fs-extra "^4.0.1" + get-port "^3.2.0" glob "^7.1.2" + glob-parent "^3.1.0" globby "^6.1.0" graceful-fs "^4.1.11" - inquirer "^3.0.6" + inquirer "^3.2.2" is-ci "^1.0.10" - load-json-file "^2.0.0" + load-json-file "^3.0.0" lodash "^4.17.4" minimatch "^3.0.4" - npmlog "^4.1.0" + npmlog "^4.1.2" p-finally "^1.0.0" path-exists "^3.0.0" read-cmd-shim "^1.0.1" read-pkg "^2.0.0" rimraf "^2.6.1" - safe-buffer "^5.0.1" - semver "^5.1.0" + safe-buffer "^5.1.1" + semver "^5.4.1" signal-exit "^3.0.2" strong-log-transformer "^1.0.6" temp-write "^3.3.0" - write-file-atomic "^2.1.0" - write-json-file "^2.1.0" - write-pkg "^3.0.1" - yargs "^8.0.1" + write-file-atomic "^2.3.0" + write-json-file "^2.2.0" + write-pkg "^3.1.0" + yargs "^8.0.2" leven@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" -levn@^0.3.0, levn@~0.3.0: +levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" dependencies: @@ -2254,6 +1913,15 @@ load-json-file@^2.0.0: pify "^2.0.0" strip-bom "^3.0.0" +load-json-file@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-3.0.0.tgz#7eb3735d983a7ed2262ade4ff769af5369c5c440" + dependencies: + graceful-fs "^4.1.2" + parse-json "^3.0.0" + pify "^2.0.0" + strip-bom "^3.0.0" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -2265,10 +1933,6 @@ lodash._reinterpolate@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" -lodash.cond@^4.3.0: - version "4.5.2" - resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" - lodash.template@^4.0.2: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" @@ -2310,9 +1974,9 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" -magic-string@^0.19.0: - version "0.19.1" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.19.1.tgz#14d768013caf2ec8fdea16a49af82fc377e75201" +magic-string@^0.22.4: + version "0.22.4" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.4.tgz#31039b4e40366395618c1d6cf8193c53917475ff" dependencies: vlq "^0.2.1" @@ -2407,7 +2071,7 @@ minimist@^1.1.1, minimist@^1.1.3: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: +mkdirp@^0.5.1, mkdirp@~0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -2425,10 +2089,6 @@ ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" -mute-stream@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" - mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" @@ -2471,7 +2131,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npmlog@^4.1.0: +npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" dependencies: @@ -2509,10 +2169,6 @@ once@^1.3.0, once@^1.4.0: dependencies: wrappy "1" -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -2526,7 +2182,7 @@ optimist@^0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" -optionator@^0.8.1, optionator@^0.8.2: +optionator@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" dependencies: @@ -2596,10 +2252,20 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +parse-json@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-3.0.0.tgz#fa6f47b18e23826ead32f263e744d0e1e847fb13" + dependencies: + error-ex "^1.3.1" + parse5@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -2614,10 +2280,6 @@ path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - path-key@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -2658,19 +2320,9 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" -pkg-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" - dependencies: - find-up "^1.0.0" - -pluralize@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" - -postman-jsdoc-theme@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/postman-jsdoc-theme/-/postman-jsdoc-theme-0.0.2.tgz#e6a1a1a4c814506bc1671b19fca2156a73442df0" +postman-jsdoc-theme@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/postman-jsdoc-theme/-/postman-jsdoc-theme-0.0.3.tgz#60e4fbf3b2175f772520b3c978c07a8bd32b5829" prelude-ls@~1.1.2: version "1.1.2" @@ -2680,6 +2332,10 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +prettier@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.6.1.tgz#850f411a3116226193e32ea5acfc21c0f9a76d7d" + pretty-format@^20.0.3: version "20.0.3" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-20.0.3.tgz#020e350a560a1fe1a98dc3beb6ccffb386de8b14" @@ -2695,10 +2351,6 @@ process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" -progress@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" - prr@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" @@ -2774,20 +2426,6 @@ readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2: string_decoder "~1.0.3" util-deprecate "~1.0.1" -readline2@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - mute-stream "0.0.5" - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - dependencies: - resolve "^1.1.6" - redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -2859,33 +2497,21 @@ require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" -require-uncached@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" - resolve@1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.2.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.3.2: version "1.3.3" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5" dependencies: path-parse "^1.0.5" -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" +resolve@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86" dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" + path-parse "^1.0.5" restore-cursor@^2.0.0: version "2.0.0" @@ -2900,20 +2526,20 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@^2.2.8, rimraf@^2.6.1: +rimraf@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" dependencies: glob "^7.0.5" -rollup-plugin-commonjs@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.0.2.tgz#98b1589bfe32a6c0f67790b60c0b499972afed89" +rollup-plugin-commonjs@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.2.0.tgz#d7b16ebb9a36b754df888fc552dfa775c1174f9d" dependencies: - acorn "^4.0.1" - estree-walker "^0.3.0" - magic-string "^0.19.0" - resolve "^1.1.7" + acorn "^5.1.1" + estree-walker "^0.5.0" + magic-string "^0.22.4" + resolve "^1.4.0" rollup-pluginutils "^2.0.1" rollup-plugin-json@^2.1.1: @@ -2944,17 +2570,9 @@ rollup-pluginutils@^2.0.1: estree-walker "^0.3.0" micromatch "^2.3.11" -rollup@^0.43.0: - version "0.43.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.43.0.tgz#b36bdb75fa5e0823b6de8aee18ff7b5655520543" - dependencies: - source-map-support "^0.4.0" - -run-async@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" - dependencies: - once "^1.3.0" +rollup@^0.49.2: + version "0.49.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.49.2.tgz#a18f07595cde3b11875c9fece45b25ad3b220d1a" run-async@^2.2.0: version "2.3.0" @@ -2972,11 +2590,7 @@ rx-lite@*, rx-lite@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" -rx-lite@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" - -safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -2996,10 +2610,14 @@ sax@^1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" -"semver@2 || 3 || 4 || 5", semver@5.3.0, semver@^5.0.1, semver@^5.1.0, semver@^5.3.0: +"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" +semver@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -3014,14 +2632,6 @@ shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" -shelljs@^0.7.5: - version "0.7.8" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" - dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" - shellwords@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.0.tgz#66afd47b6a12932d9071cbfd98a52e785cd0ba14" @@ -3034,10 +2644,6 @@ slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - slide@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -3060,7 +2666,7 @@ sort-keys@^2.0.0: dependencies: is-plain-obj "^1.0.0" -source-map-support@^0.4.0, source-map-support@^0.4.2: +source-map-support@^0.4.2: version "0.4.15" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1" dependencies: @@ -3147,6 +2753,13 @@ string-width@^2.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string_decoder@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" @@ -3189,10 +2802,6 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - strong-log-transformer@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-1.0.6.tgz#f7fb93758a69a571140181277eea0c2eb1301fa3" @@ -3213,21 +2822,16 @@ supports-color@^3.1.2: dependencies: has-flag "^1.0.0" +supports-color@^4.0.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" + dependencies: + has-flag "^2.0.0" + symbol-tree@^3.2.1: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" -table@^3.7.8: - version "3.8.3" - resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" - dependencies: - ajv "^4.7.0" - ajv-keywords "^1.0.0" - chalk "^1.1.1" - lodash "^4.0.0" - slice-ansi "0.0.4" - string-width "^2.0.0" - temp-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" @@ -3264,10 +2868,6 @@ text-extensions@^1.0.0: version "1.5.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.5.0.tgz#d1cb2d14b5d0bc45bfdca8a08a473f68c7eb0cbc" -text-table@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - throat@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/throat/-/throat-3.2.0.tgz#50cb0670edbc40237b9e347d7e1f88e4620af836" @@ -3319,10 +2919,6 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" -tryit@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" - tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -3367,12 +2963,6 @@ universalify@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.0.tgz#9eb1c4651debcc670cc94f1a75762332bb967778" -user-home@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" - dependencies: - os-homedir "^1.0.0" - util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -3489,7 +3079,7 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -write-file-atomic@^2.0.0, write-file-atomic@^2.1.0: +write-file-atomic@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.1.0.tgz#1769f4b551eedce419f0505deae2e26763542d37" dependencies: @@ -3497,7 +3087,15 @@ write-file-atomic@^2.0.0, write-file-atomic@^2.1.0: imurmurhash "^0.1.4" slide "^1.1.5" -write-json-file@^2.1.0, write-json-file@^2.2.0: +write-file-atomic@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write-json-file@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-2.2.0.tgz#51862506bbb3b619eefab7859f1fd6c6d0530876" dependencies: @@ -3508,24 +3106,18 @@ write-json-file@^2.1.0, write-json-file@^2.2.0: sort-keys "^1.1.1" write-file-atomic "^2.0.0" -write-pkg@^3.0.1: +write-pkg@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-3.1.0.tgz#030a9994cc9993d25b4e75a9f1a1923607291ce9" dependencies: sort-keys "^2.0.0" write-json-file "^2.2.0" -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - dependencies: - mkdirp "^0.5.1" - xml-name-validator@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" -xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: +xtend@^4.0.1, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -3567,7 +3159,7 @@ yargs@^7.0.2: y18n "^3.2.1" yargs-parser "^5.0.0" -yargs@^8.0.1: +yargs@^8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" dependencies: