tonal-array
Advanced tools
Comparing version 0.50.0 to 0.50.1
@@ -1,205 +0,208 @@ | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('tonal-pitch'), require('tonal-notation'), require('tonal-transpose')) : | ||
typeof define === 'function' && define.amd ? define(['exports', 'tonal-pitch', 'tonal-notation', 'tonal-transpose'], factory) : | ||
(factory((global.array = global.array || {}),global.tonalPitch,global.tonalNotation,global.tonalTranspose)); | ||
}(this, function (exports,tonalPitch,tonalNotation,tonalTranspose) { 'use strict'; | ||
'use strict'; | ||
function id (x) { return x } | ||
var tonalPitch = require('tonal-pitch'); | ||
var tonalNotation = require('tonal-notation'); | ||
var tonalTranspose = require('tonal-transpose'); | ||
// items can be separated by spaces, bars and commas | ||
var SEP = /\s*\|\s*|\s*,\s*|\s+/ | ||
function id (x) { return x } | ||
/** | ||
* Convert anything to array. Speifically, split string separated by spaces, | ||
* commas or bars. The arrays are passed without modifications and the rest of | ||
* the objects are wrapped. | ||
* | ||
* This function always returns an array (null or undefined values are converted | ||
* to empty arrays) | ||
* | ||
* Thanks to this function, the rest of the functions of this module accepts | ||
* any object (or more useful: strings) as an array parameter. | ||
* | ||
* @param {*} source - the thing to get an array from | ||
* @return {Array} the object as an array | ||
* | ||
* @example | ||
* import { asArr } from 'tonal-arrays' | ||
* asArr('C D E F G') // => ['C', 'D', 'E', 'F', 'G'] | ||
*/ | ||
function asArr (src) { | ||
return tonalNotation.isArr(src) ? src | ||
: typeof src === 'string' ? src.trim().split(SEP) | ||
: (src === null || typeof src === 'undefined') ? [] | ||
: [ src ] | ||
} | ||
// items can be separated by spaces, bars and commas | ||
var SEP = /\s*\|\s*|\s*,\s*|\s+/ | ||
/** | ||
* Map an array with a function. Basically the same as the JavaScript standard | ||
* `array.map` but with two enhacements: | ||
* - Arrays can be expressed as strings (see [asArr]) | ||
* - This function can be partially applied. This is useful to create _mapped_ | ||
* versions of single element functions. For an excellent introduction of | ||
* the adventages [read this](https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch4.html) | ||
* | ||
* @param {Function} fn - the function | ||
* @param {Array|String} arr - the array to be mapped | ||
* @return {Array} | ||
* @example | ||
* var arr = require('tonal-arr') | ||
* var toUp = arr.map(function(e) { return e.toUpperCase() }) | ||
* toUp('a b c') // => ['A', 'B', 'C'] | ||
* | ||
* @example | ||
* var tonal = require('tonal') | ||
* tonal.map(tonal.transpose('M3'), 'C D E') // => ['E', 'F#', 'G#'] | ||
*/ | ||
function map (fn, list) { | ||
return arguments.length > 1 ? map(fn)(list) | ||
: function (l) { return asArr(l).map(fn) } | ||
} | ||
/** | ||
* Convert anything to array. Speifically, split string separated by spaces, | ||
* commas or bars. The arrays are passed without modifications and the rest of | ||
* the objects are wrapped. | ||
* | ||
* This function always returns an array (null or undefined values are converted | ||
* to empty arrays) | ||
* | ||
* Thanks to this function, the rest of the functions of this module accepts | ||
* any object (or more useful: strings) as an array parameter. | ||
* | ||
* @param {*} source - the thing to get an array from | ||
* @return {Array} the object as an array | ||
* | ||
* @example | ||
* import { asArr } from 'tonal-arrays' | ||
* asArr('C D E F G') // => ['C', 'D', 'E', 'F', 'G'] | ||
*/ | ||
function asArr (src) { | ||
return tonalNotation.isArr(src) ? src | ||
: typeof src === 'string' ? src.trim().split(SEP) | ||
: (src === null || typeof src === 'undefined') ? [] | ||
: [ src ] | ||
} | ||
/** | ||
* Filter an array with a function. Again, almost the same as JavaScript standard | ||
* filter function but: | ||
* - It accepts strings as arrays | ||
* - Can be partially applied | ||
* | ||
* @param {Function} fn | ||
* @param {String|Array} arr | ||
* @return {Array} | ||
*/ | ||
function filter (fn, list) { | ||
return arguments.length > 1 ? filter(fn)(list) | ||
: function (l) { return asArr(l).filter(fn) } | ||
} | ||
/** | ||
* Map an array with a function. Basically the same as the JavaScript standard | ||
* `array.map` but with two enhacements: | ||
* - Arrays can be expressed as strings (see [asArr]) | ||
* - This function can be partially applied. This is useful to create _mapped_ | ||
* versions of single element functions. For an excellent introduction of | ||
* the adventages [read this](https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch4.html) | ||
* | ||
* @param {Function} fn - the function | ||
* @param {Array|String} arr - the array to be mapped | ||
* @return {Array} | ||
* @example | ||
* var arr = require('tonal-arr') | ||
* var toUp = arr.map(function(e) { return e.toUpperCase() }) | ||
* toUp('a b c') // => ['A', 'B', 'C'] | ||
* | ||
* @example | ||
* var tonal = require('tonal') | ||
* tonal.map(tonal.transpose('M3'), 'C D E') // => ['E', 'F#', 'G#'] | ||
*/ | ||
function map (fn, list) { | ||
return arguments.length > 1 ? map(fn)(list) | ||
: function (l) { return asArr(l).map(fn) } | ||
} | ||
// #### Transform lists in array notation | ||
function asPitchStr (p) { return tonalPitch.strPitch(p) || p } | ||
function listToStr (v) { | ||
return tonalPitch.isPitch(v) ? tonalPitch.strPitch(v) | ||
: tonalNotation.isArr(v) ? v.map(asPitchStr) | ||
: v | ||
} | ||
/** | ||
* Filter an array with a function. Again, almost the same as JavaScript standard | ||
* filter function but: | ||
* - It accepts strings as arrays | ||
* - Can be partially applied | ||
* | ||
* @param {Function} fn | ||
* @param {String|Array} arr | ||
* @return {Array} | ||
*/ | ||
function filter (fn, list) { | ||
return arguments.length > 1 ? filter(fn)(list) | ||
: function (l) { return asArr(l).filter(fn) } | ||
} | ||
/** | ||
* Decorates a function to so it's first parameter is an array of pitches in | ||
* array notation. Also, if the return value is a pitch or an array of pitches | ||
* in array notation, it convert backs to strings. | ||
* | ||
* @function | ||
* @param {Function} fn - the function to decorate | ||
* @return {Function} the decorated function | ||
* @example | ||
* import { listFn } from 'tonal-arrays' | ||
* var octUp = listFn((p) => { p[2] = p[2] + 1; return p[2] }) | ||
* octUp('C2 D2 E2') // => ['C3', 'D3', 'E3'] | ||
*/ | ||
var listFn = (fn) => (src) => { | ||
var param = asArr(src).map(tonalPitch.asPitch) | ||
var result = fn(param) | ||
return listToStr(result) | ||
} | ||
// #### Transform lists in array notation | ||
function asPitchStr (p) { return tonalPitch.strPitch(p) || p } | ||
function listToStr (v) { | ||
return tonalPitch.isPitch(v) ? tonalPitch.strPitch(v) | ||
: tonalNotation.isArr(v) ? v.map(asPitchStr) | ||
: v | ||
} | ||
/** | ||
* Given an array of intervals, create a function that harmonizes a | ||
* note with this intervals. | ||
* | ||
* @function | ||
* @param {Array|String} ivls - the array of intervals | ||
* @return {Function} The harmonizer | ||
* @example | ||
* import { harmonizer } from 'tonal-arrays' | ||
* var maj7 = harmonizer('P1 M3 P5 M7') | ||
* maj7('C') // => ['C', 'E', 'G', 'B'] | ||
*/ | ||
var harmonizer = (list) => (pitch) => { | ||
return listFn((list) => list.map(tonalTranspose.tr(pitch || 'P1')).filter(id))(list) | ||
/** | ||
* Decorates a function to so it's first parameter is an array of pitches in | ||
* array notation. Also, if the return value is a pitch or an array of pitches | ||
* in array notation, it convert backs to strings. | ||
* | ||
* @function | ||
* @param {Function} fn - the function to decorate | ||
* @return {Function} the decorated function | ||
* @example | ||
* import { listFn } from 'tonal-arrays' | ||
* var octUp = listFn((p) => { p[2] = p[2] + 1; return p[2] }) | ||
* octUp('C2 D2 E2') // => ['C3', 'D3', 'E3'] | ||
*/ | ||
function listFn (fn) { | ||
return function (list) { | ||
var arr = asArr(list).map(tonalPitch.asPitch) | ||
var res = fn(arr) | ||
return listToStr(res) | ||
} | ||
} | ||
/** | ||
* Harmonizes a note with an array of intervals. It's a layer of sintatic | ||
* sugar over `harmonizer`. | ||
* | ||
* @function | ||
* @param {String|Array} ivl - the array of intervals | ||
* @param {String|Pitch} note - the note to be harmonized | ||
* @return {Array} the resulting notes | ||
* @example | ||
* var tonal = require('tonal') | ||
* tonal.harmonise('P1 M3 P5 M7', 'C') // => ['C', 'E', 'G', 'B'] | ||
*/ | ||
var harmonize = function (list, pitch) { | ||
return arguments.length > 1 ? harmonizer(list)(pitch) : harmonizer(list) | ||
/** | ||
* Given an array of intervals, create a function that harmonizes a | ||
* note with this intervals. | ||
* | ||
* @param {Array|String} ivls - the array of intervals | ||
* @return {Function} The harmonizer | ||
* @example | ||
* import { harmonizer } from 'tonal-arrays' | ||
* var maj7 = harmonizer('P1 M3 P5 M7') | ||
* maj7('C') // => ['C', 'E', 'G', 'B'] | ||
*/ | ||
function harmonizer (list) { | ||
return function (tonic) { | ||
return listFn(function (arr) { | ||
return arr.map(tonalTranspose.tr(tonic || 'P1')).filter(id) | ||
})(list) | ||
} | ||
} | ||
// a custom height function that | ||
// - returns -Infinity for non-pitch objects | ||
// - assumes pitch classes has octave -10 (so are sorted before that notes) | ||
var objHeight = function (p) { | ||
if (!p) return -Infinity | ||
var f = p[1] * 7 | ||
var o = tonalNotation.isNum(p[2]) ? p[2] : -Math.floor(f / 12) - 10 | ||
return f + o * 12 | ||
} | ||
/** | ||
* Harmonizes a note with an array of intervals. It's a layer of sintatic | ||
* sugar over `harmonizer`. | ||
* | ||
* @function | ||
* @param {String|Array} ivl - the array of intervals | ||
* @param {String|Pitch} note - the note to be harmonized | ||
* @return {Array} the resulting notes | ||
* @example | ||
* var tonal = require('tonal') | ||
* tonal.harmonise('P1 M3 P5 M7', 'C') // => ['C', 'E', 'G', 'B'] | ||
*/ | ||
var harmonize = function (list, pitch) { | ||
return arguments.length > 1 ? harmonizer(list)(pitch) : harmonizer(list) | ||
} | ||
var ascComp = (a, b) => objHeight(a) - objHeight(b) | ||
var descComp = (a, b) => -ascComp(a, b) | ||
// a custom height function that | ||
// - returns -Infinity for non-pitch objects | ||
// - assumes pitch classes has octave -10 (so are sorted before that notes) | ||
var objHeight = function (p) { | ||
if (!p) return -Infinity | ||
var f = p[1] * 7 | ||
var o = tonalNotation.isNum(p[2]) ? p[2] : -Math.floor(f / 12) - 10 | ||
return f + o * 12 | ||
} | ||
/** | ||
* Sort an array or notes or intervals. It uses the JavaScript standard sort | ||
* function. | ||
* | ||
* @param {Boolean|Function} comp - the comparator. `true` means use an | ||
* ascending comparator, `false` a descending comparator, or you can pass a | ||
* custom comparator (that receives pitches in array notation) | ||
* @param {Array|String} arr - the array of notes or intervals | ||
* @example | ||
* import { sort } from 'tonal-arrays' | ||
* sort(true, 'D E C') // => ['C', 'D', 'E'] | ||
* @example | ||
* var tonal = require('tonal') | ||
* tonal.sort(false, 'D E C') // => ['E', 'D', 'C'] | ||
*/ | ||
function sort (comp, list) { | ||
if (arguments.length > 1) return sort(comp)(list) | ||
var fn = comp === true || comp === null ? ascComp | ||
: comp === false ? descComp : comp | ||
return listFn((arr) => arr.sort(fn)) | ||
} | ||
var ascComp = (a, b) => objHeight(a) - objHeight(b) | ||
var descComp = (a, b) => -ascComp(a, b) | ||
/** | ||
* Randomizes the order of the specified array using the Fisher–Yates shuffle. | ||
* | ||
* @function | ||
* @param {Array|String} arr - the array | ||
* @return {Array} the shuffled array | ||
* | ||
* @example | ||
* import { shuffle } from 'tonal-arrays' | ||
* @example | ||
* var tonal = require('tonal') | ||
* tonal.shuffle('C D E F') | ||
*/ | ||
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 | ||
} | ||
return arr | ||
}) | ||
/** | ||
* Sort an array or notes or intervals. It uses the JavaScript standard sort | ||
* function. | ||
* | ||
* @param {Boolean|Function} comp - the comparator. `true` means use an | ||
* ascending comparator, `false` a descending comparator, or you can pass a | ||
* custom comparator (that receives pitches in array notation) | ||
* @param {Array|String} arr - the array of notes or intervals | ||
* @example | ||
* import { sort } from 'tonal-arrays' | ||
* sort(true, 'D E C') // => ['C', 'D', 'E'] | ||
* @example | ||
* var tonal = require('tonal') | ||
* tonal.sort(false, 'D E C') // => ['E', 'D', 'C'] | ||
*/ | ||
function sort (comp, list) { | ||
if (arguments.length > 1) return sort(comp)(list) | ||
var fn = comp === true || comp === null ? ascComp | ||
: comp === false ? descComp : comp | ||
return listFn((arr) => arr.sort(fn)) | ||
} | ||
exports.asArr = asArr; | ||
exports.map = map; | ||
exports.filter = filter; | ||
exports.listFn = listFn; | ||
exports.harmonizer = harmonizer; | ||
exports.harmonize = harmonize; | ||
exports.sort = sort; | ||
exports.shuffle = shuffle; | ||
/** | ||
* Randomizes the order of the specified array using the Fisher–Yates shuffle. | ||
* | ||
* @function | ||
* @param {Array|String} arr - the array | ||
* @return {Array} the shuffled array | ||
* | ||
* @example | ||
* import { shuffle } from 'tonal-arrays' | ||
* @example | ||
* var tonal = require('tonal') | ||
* tonal.shuffle('C D E F') | ||
*/ | ||
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 | ||
} | ||
return arr | ||
}) | ||
})); | ||
exports.asArr = asArr; | ||
exports.map = map; | ||
exports.filter = filter; | ||
exports.listFn = listFn; | ||
exports.harmonizer = harmonizer; | ||
exports.harmonize = harmonize; | ||
exports.sort = sort; | ||
exports.shuffle = shuffle; |
19
index.js
@@ -95,6 +95,8 @@ import { asPitch, isPitch, strPitch } from 'tonal-pitch' | ||
*/ | ||
export var listFn = (fn) => (src) => { | ||
var param = asArr(src).map(asPitch) | ||
var result = fn(param) | ||
return listToStr(result) | ||
export function listFn (fn) { | ||
return function (list) { | ||
var arr = asArr(list).map(asPitch) | ||
var res = fn(arr) | ||
return listToStr(res) | ||
} | ||
} | ||
@@ -106,3 +108,2 @@ | ||
* | ||
* @function | ||
* @param {Array|String} ivls - the array of intervals | ||
@@ -115,4 +116,8 @@ * @return {Function} The harmonizer | ||
*/ | ||
export var harmonizer = (list) => (pitch) => { | ||
return listFn((list) => list.map(tr(pitch || 'P1')).filter(id))(list) | ||
export function harmonizer (list) { | ||
return function (tonic) { | ||
return listFn(function (arr) { | ||
return arr.map(tr(tonic || 'P1')).filter(id) | ||
})(list) | ||
} | ||
} | ||
@@ -119,0 +124,0 @@ |
{ | ||
"name": "tonal-array", | ||
"version": "0.50.0", | ||
"version": "0.50.1", | ||
"description": "Create and manipulate arrays of notes and intervals", | ||
"keywords": [ | ||
"note", | ||
"music", | ||
"theory", | ||
"array", | ||
"numbers", | ||
"tonal" | ||
], | ||
"scripts": { | ||
"pretest": "rm -rf build && mkdir build && rollup -f umd -n array -o build/tonal-array.js -- index.js", | ||
"test": "tape 'test/*.js'" | ||
"pretest": "rm -rf build && mkdir build && rollup -f cjs -n array -o build/tonal-array.js -- index.js", | ||
"test": "tape 'test/*.js'", | ||
"prepublish": "npm test" | ||
}, | ||
@@ -15,0 +16,0 @@ "main": "build/tonal-array.js", |
429
29710