├── .gitignore ├── .travis.yml ├── codeclimate.yml ├── index.js ├── test ├── names-test.js ├── fromName-test.js ├── degrees-test.js ├── dictionary-test.js ├── benchmark │ └── benchmark-scale.js └── scale-test.js ├── names.js ├── dict ├── aliases.json └── scales.json ├── fromName.js ├── CHANGELOG ├── scale.js ├── package.json ├── degrees.js ├── dictionary.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | npm-debug.log 4 | dist/ 5 | coverage/ 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 4.0.0 4 | - "0.10" 5 | - "0.12" 6 | - "iojs" 7 | -------------------------------------------------------------------------------- /codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | eslint: 3 | enabled: true 4 | ratings: 5 | paths: 6 | - "**.js" 7 | exclude_paths: 8 | - test/**/* 9 | - dist/* 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var scale = require('./scale') 4 | scale.dictionary = require('./dictionary') 5 | scale.fromName = require('./fromName') 6 | scale.names = require('./names') 7 | 8 | module.exports = scale 9 | -------------------------------------------------------------------------------- /test/names-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows') 2 | var assert = require('assert') 3 | var names = require('../names') 4 | 5 | vows.describe('names').addBatch({ 6 | 'main names': function () { 7 | assert.deepEqual(names().length, 89) 8 | }, 9 | 'aliases names': function () { 10 | assert.deepEqual(names(true).length, 108) 11 | } 12 | }).export(module) 13 | -------------------------------------------------------------------------------- /test/fromName-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows') 2 | var assert = require('assert') 3 | var fromName = require('../fromName') 4 | 5 | vows.describe('fromName').addBatch({ 6 | 'all scales': function () { 7 | assert.deepEqual(fromName('bebop locrian', 'C'), [ 'C', 'Db', 'Eb', 'F', 'Gb', 'G', 'Ab', 'Bb' ]) 8 | }, 9 | 'parially applied': function () { 10 | var kumoi = fromName('kumoi') 11 | assert.deepEqual(kumoi('G'), ['G', 'A', 'Bb', 'D', 'E']) 12 | } 13 | }).export(module) 14 | -------------------------------------------------------------------------------- /names.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var scales = require('./dict/scales.json') 4 | var aliases = require('./dict/aliases.json') 5 | 6 | /** 7 | * Get all scale names available 8 | * 9 | * @name names 10 | * @function 11 | * @param {Boolean} withAliases - set to `true` to get aliases names 12 | * @return {Array} the list of all scale names 13 | * 14 | * @example 15 | * scale.names() // => ['major', 'minor', ...] 16 | */ 17 | module.exports = function (withAliases) { 18 | var names = Object.keys(scales) 19 | return withAliases ? names.concat(Object.keys(aliases)) : names 20 | } 21 | -------------------------------------------------------------------------------- /test/degrees-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows') 2 | var assert = require('assert') 3 | var degrees = require('../degrees') 4 | 5 | vows.describe('degrees').addBatch({ 6 | 'get specified only': function () { 7 | assert.deepEqual(degrees('1 3 5', 'C D E F G A B'), [ 'C', 'E', 'G' ]) 8 | assert.deepEqual(degrees('1 3 6', 'C D E F G B'), [ 'C', 'E', null ]) 9 | }, 10 | 'different order': function () { 11 | assert.deepEqual(degrees('1 5 3', 'C D E F G A B'), [ 'C', 'G', 'E' ]) 12 | assert.deepEqual(degrees('1 5 2 6', 'C D E F G A B'), [ 'C', 'G', 'D', 'A' ]) 13 | } 14 | }).export(module) 15 | -------------------------------------------------------------------------------- /test/dictionary-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows') 2 | var assert = require('assert') 3 | var dictionary = require('../dictionary') 4 | 5 | vows.describe('dictionary').addBatch({ 6 | 'no aliases': function () { 7 | var scales = dictionary({ major: '1 2 3 4 5 6 7', minor: '1 2 3b 4 5 6b 7b' }) 8 | assert.equal(scales('major', 'D').join(' '), 'D E F# G A B C#') 9 | }, 10 | 'alias': function () { 11 | var scales = dictionary({ major: '1 2 3 4 5 6 7', minor: '1 2 3b 4 5 6b 7b' }, 12 | {eolian: 'minor'}) 13 | assert.deepEqual(scales('eolian', 'D'), scales('minor', 'D')) 14 | } 15 | }).export(module) 16 | -------------------------------------------------------------------------------- /test/benchmark/benchmark-scale.js: -------------------------------------------------------------------------------- 1 | // BENCHMARKS 2 | // [0.10.0] build scale x 70,716 ops/sec ±0.50% (101 runs sampled) 3 | // [0.11.0] build scale x 54,052 ops/sec ±0.60% (98 runs sampled) 4 | var Benchmark = require('benchmark') 5 | var scale = require('../../') 6 | 7 | console.log(scale('C D E F G A B', 'D')) 8 | 9 | new Benchmark.Suite() 10 | .add('build scale', function () { 11 | scale('C D E F G A B', 'D') 12 | }) 13 | .on('cycle', function (event) { 14 | console.log(String(event.target)) 15 | }) 16 | .on('complete', function () { 17 | console.log('Fastest is ' + this.filter('fastest').pluck('name')) 18 | }) 19 | .run({ 'async': true }) 20 | -------------------------------------------------------------------------------- /dict/aliases.json: -------------------------------------------------------------------------------- 1 | { 2 | "dominant": "mixolydian", 3 | "super locrian": "altered", 4 | "diminished whole tone": "altered", 5 | "arabian": "locrian major", 6 | "ionian": "major", 7 | "minor": "aeolian", 8 | "pomeroy": "altered", 9 | "pentatonic": "major pentatonic", 10 | "minor seven flat five pentatonic": "locrian pentatonic", 11 | "chinese": "lydian pentatonic", 12 | "kumoi": "flat three pentatonic", 13 | "blues": "minor blues", 14 | "gypsy": "double harmonic major", 15 | "hindu": "melodic minor fifth mode", 16 | "indian": "mixolydian pentatonic", 17 | "dorian b2": "neopolitan major", 18 | "lydian b7": "lydian dominant", 19 | "mixolydian b6": "melodic minor fifth mode", 20 | "phrygian major": "spanish" 21 | } 22 | -------------------------------------------------------------------------------- /fromName.js: -------------------------------------------------------------------------------- 1 | var scales = require('./dict/scales.json') 2 | var aliases = require('./dict/aliases.json') 3 | var dictionary = require('./dictionary') 4 | 5 | /** 6 | * Build a scale using a name and a tonic 7 | * 8 | * It uses a dictionary of scales (see dict directory) 9 | * 10 | * Can be partially applied (see example) 11 | * 12 | * @name fromName 13 | * @function 14 | * @param {String} name - the name of the scale 15 | * @param {String|Array} tonic - the tonic of the scale 16 | * 17 | * @example 18 | * scale.fromName('bebop locrian', 'C') // => [ 'C', 'Db', 'Eb', 'F', 'Gb', 'G', 'Ab', 'Bb' ] 19 | * var kumoi = scale.fromName('kumoi') 20 | * kumoi('G') // => ['G', 'A', 'Bb', 'D', 'E'] 21 | */ 22 | module.exports = dictionary(scales, aliases) 23 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | 3 | [next] 4 | 5 | [0.12.0] 6 | - No depends on music-gamut to tranform pitches and intervals 7 | 8 | [0.11.0] 9 | - The scales are now sorted by freq 10 | 11 | [0.10.0] 12 | - Add degrees function 13 | 14 | [0.9.0] 15 | - Add two sugar functions: fromName and names 16 | - Replace fromName with dictionary 17 | - Move dictionaries to its own directory 18 | 19 | [0.8.0] 20 | - First functional version 21 | - Full rewrite (based on tonal) 22 | 23 | [0.7.0] 24 | - First OO version 25 | 26 | BENCHMARKS 27 | [0.12.0] build scale x 52,723 ops/sec ±3.41% (89 runs sampled) 28 | [0.11.0] build scale x 54,052 ops/sec ±0.60% (98 runs sampled) 29 | [0.10.0] build scale x 70,716 ops/sec ±0.50% (101 runs sampled) 30 | 31 | SIZE 32 | [0.12.0] 34236 / 15126 33 | 34 | TAGS 35 | git tag 0.1.0 36 | git push origin master --tags 37 | -------------------------------------------------------------------------------- /scale.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var curry = require('curry') 4 | var gamut = require('music-gamut') 5 | var transpose = gamut.asNotes(gamut.add) 6 | 7 | /** 8 | * Build a scale from a source and a tonic. A scale is an array of notes (or 9 | * intervals if tonic is null) ordered by frequency 10 | * 11 | * A source can be a list of intervals or notes. The tonic must be 12 | * a pitch (with or without octave) or null to get the scale intervals 13 | * 14 | * This function is currified, so you can partially apply the function passing 15 | * one parameter instead of two (see example) 16 | * 17 | * @param {Array} source - the list of intervals or notes 18 | * @param {String} tonic - the tonic of the scale 19 | * @return {Array} the list of notes 20 | * 21 | * @example 22 | * scale('1 2 3 5 6', 'G') // => ['G', 'A', 'B', 'D', 'E'] 23 | * var dorian = scale('D E F G A B C') 24 | * dorian('C4') 25 | */ 26 | function scale (src, tonic) { 27 | var intervals = gamut.set(src) 28 | return tonic ? transpose(tonic, intervals) : gamut.asIntervals(intervals) 29 | } 30 | 31 | module.exports = curry(scale) 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "music-scale", 3 | "version": "0.12.0", 4 | "description": "Music scales made easy", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "vows --spec test/*.js", 8 | "docs": "docme README.md", 9 | "dist": "browserify index.js > ./dist/music-scale.js && browserify index.js | uglifyjs > ./dist/music-scale.min.js", 10 | "coverage": "istanbul cover test/tests.js", 11 | "benchmarks": "node ./test/benchmark/*.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/danigb/music-scale.git" 16 | }, 17 | "keywords": [ 18 | "music", 19 | "scale", 20 | "scales", 21 | "theory", 22 | "harmony", 23 | "jazz", 24 | "midi" 25 | ], 26 | "author": "danigb ", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/danigb/music-scale/issues" 30 | }, 31 | "homepage": "https://github.com/danigb/music-scale#readme", 32 | "devDependencies": { 33 | "benchmark": "^1.0.0", 34 | "vows": "^0.8.1" 35 | }, 36 | "dependencies": { 37 | "curry": "^1.2.0", 38 | "music-gamut": "^0.2.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /degrees.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var gamut = require('music-gamut') 4 | var transpose = gamut.asNotes(gamut.add) 5 | 6 | function find (pitchNum, array) { 7 | for (var i = 0, len = array.length; i < len; i++) { 8 | if (array[i][0] === pitchNum) return array[i] 9 | } 10 | return null 11 | } 12 | 13 | /** 14 | * Get the degrees of a scale 15 | * 16 | * The resulting array will contain the notes in the same order as degrees. 17 | * If a given degree is not present in the scale, the result will contain a 18 | * null in that position. 19 | * 20 | * @name degrees 21 | * @function 22 | * @param {Array|String} degrees - the degrees numbers 23 | * @param {Array|String} notes - the scale notes 24 | * @return {Array} the notes of the given degrees (or null if not present) 25 | * 26 | * @example 27 | * scale.degrees('1 3 5', 'C D E F G A B') // => [ 'C', 'E', 'G' ] 28 | * scale.degrees('1 5 2 6', 'C D E F G A B') // => [ 'C', 'G', 'D', 'A' ] 29 | * scale.degrees('1 2 6', 'C D E F G') // => [ 'C', 'D', null ] 30 | */ 31 | module.exports = function (degrees, notes) { 32 | var tonic = gamut.asArray(notes)[0] 33 | var set = gamut.set(notes) 34 | var numbers = gamut.asArray(degrees).map(function (i) { return +i - 1 }) 35 | var selected = numbers.map(function (num) { 36 | return find(num, set) 37 | }) 38 | return transpose(tonic, selected) 39 | } 40 | -------------------------------------------------------------------------------- /dictionary.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var curry = require('curry') 4 | var gamut = require('music-gamut') 5 | var scale = require('./scale') 6 | 7 | function mapValues (hash, fn) { 8 | return Object.keys(hash).reduce(function (data, name) { 9 | data[name] = fn(hash[name]) 10 | return data 11 | }, {}) 12 | } 13 | 14 | /** 15 | * Create a scale builder function from a hash of data 16 | * 17 | * A scale builder is a function that given a names and a tonic, returns 18 | * a scale (array). It can be partially applied. 19 | * 20 | * @name dictionary 21 | * @function 22 | * @param {Hash} data - the data (maps names to intervals or notes) 23 | * @param {Hash} aliases - (Optional) maps names to names in the data hash 24 | * @return {Function} a function to create scales 25 | * 26 | * @example 27 | * var scales = dictionary({ major: '1 2 3 4 5 6 7', minor: '1 2 3b 4 5 6b 7b' }, {eolian: 'minor'}) 28 | * scales('major', 'C') // => ['C', 'D', 'E', 'F', 'G', 'A', 'B'] 29 | * scales('aeolian', 'A') // => ['A', 'B', 'C', 'D', 'E', 'F', 'G'] 30 | * var minor = scales('minor') 31 | * minor('D') // => ['D', 'E', 'F', 'G', 'A', 'Bb', 'C'] 32 | */ 33 | module.exports = function (hash, alias) { 34 | var data = mapValues(hash, function (src) { 35 | return gamut.set(src) 36 | }) 37 | alias = alias || {} 38 | 39 | return curry(function (name, tonic) { 40 | var intervals = data[name] || data[alias[name]] 41 | return scale(intervals, tonic) 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /test/scale-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows') 2 | var assert = require('assert') 3 | var scale = require('../scale') 4 | 5 | vows.describe('scale').addBatch({ 6 | 'build scale from intervals': function () { 7 | assert.deepEqual(scale('1 2 3 4', 'C'), ['C', 'D', 'E', 'F']) 8 | assert.deepEqual(scale('8 9 10 11', 'C2'), [ 'C2', 'D2', 'E2', 'F2' ]) 9 | }, 10 | 'build scale from notes': function () { 11 | assert.deepEqual(scale('C2 D E4 F G A B', 'D5'), ['D5', 'E5', 'F#5', 'G5', 'A5', 'B5', 'C#6']) 12 | assert.deepEqual(scale('D E F G A B C', 'C'), [ 'C', 'D', 'Eb', 'F', 'G', 'A', 'Bb' ]) 13 | }, 14 | 'remove duplicaties': function () { 15 | assert.deepEqual(scale('1 2 2 3 4 11 11#', 'C'), ['C', 'D', 'E', 'F', 'F#']) 16 | assert.deepEqual(scale('C D E C4 G5 G7 A5 D9', 'A4'), ['A4', 'B4', 'C#5', 'E5', 'F#5']) 17 | }, 18 | 'get scale intervals': function () { 19 | assert.deepEqual(scale('C D E F G A', null), [ '1P', '2M', '3M', '4P', '5P', '6M' ]) 20 | }, 21 | 'partial scale': function () { 22 | var major = scale('1 3 5') 23 | assert.deepEqual(major('D'), ['D', 'F#', 'A']) 24 | var lydian = scale('C D E F# G A B') 25 | assert.deepEqual(lydian('A'), ['A', 'B', 'C#', 'D#', 'E', 'F#', 'G#']) 26 | var aeolian = scale('A B C D E F G') 27 | assert.deepEqual(aeolian('Eb'), [ 'Eb', 'F', 'Gb', 'Ab', 'Bb', 'Cb', 'Db' ]) 28 | }, 29 | 'null scale': function () { 30 | assert.deepEqual(scale(null, 'C'), []) 31 | } 32 | }).export(module) 33 | -------------------------------------------------------------------------------- /dict/scales.json: -------------------------------------------------------------------------------- 1 | { 2 | "lydian": "1 2 3 4# 5 6 7", 3 | "major": "1 2 3 4 5 6 7", 4 | "mixolydian": "1 2 3 4 5 6 7b", 5 | "dorian": "1 2 3b 4 5 6 7b", 6 | "aeolian": "1 2 3b 4 5 6b 7b", 7 | "phrygian": "1 2b 3b 4 5 6b 7b", 8 | "locrian": "1 2b 3b 4 5b 6b 7b", 9 | "melodic minor": "1 2 3b 4 5 6 7", 10 | "melodic minor second mode": "1 2b 3b 4 5 6 7b", 11 | "lydian augmented": "1 2 3 4# 5A 6 7", 12 | "lydian dominant": "1 2 3 4# 5 6 7b", 13 | "melodic minor fifth mode": "1 2 3 4 5 6b 7b", 14 | "locrian #2": "1 2 3b 4 5b 6b 7b", 15 | "locrian major": "1 2 3 4 5b 6b 7b", 16 | "altered": "1 2b 3b 3 5b 6b 7b", 17 | "major pentatonic": "1 2 3 5 6", 18 | "lydian pentatonic": "1 3 4# 5 7", 19 | "mixolydian pentatonic": "1 3 4 5 7b", 20 | "locrian pentatonic": "1 3b 4 5b 7b", 21 | "minor pentatonic": "1 3b 4 5 7b", 22 | "minor six pentatonic": "1 3b 4 5 6", 23 | "minor hexatonic": "1 2 3b 4 5 7", 24 | "flat three pentatonic": "1 2 3b 5 6", 25 | "flat six pentatonic": "1 2 3 5 6b", 26 | "major flat two pentatonic": "1 2b 3 5 6", 27 | "whole tone pentatonic": "1 3 5b 6b 7b", 28 | "ionian pentatonic": "1 3 4 5 7", 29 | "lydian #5 pentatonic": "1 3 4# 5A 7", 30 | "lydian dominant pentatonic": "1 3 4# 5 7b", 31 | "minor #7 pentatonic": "1 3b 4 5 7", 32 | "super locrian pentatonic": "1 3b 4d 5b 7b", 33 | "in-sen": "1 2b 4 5 7b", 34 | "iwato": "1 2b 4 5b 7b", 35 | "hirajoshi": "1 2 3b 5 6b", 36 | "kumoijoshi": "1 2b 4 5 6b", 37 | "pelog": "1 2b 3b 5 6b", 38 | "vietnamese 1": "1 3b 4 5 6b", 39 | "vietnamese 2": "1 3b 4 5 7b", 40 | "prometheus": "1 2 3 4# 6 7b", 41 | "prometheus neopolitan": "1 2b 3 4# 6 7b", 42 | "ritusen": "1 2 4 5 6", 43 | "scriabin": "1 2b 3 5 6", 44 | "piongio": "1 2 4 5 6 7b", 45 | "major blues": "1 2 3b 3 5 6", 46 | "minor blues": "1 3b 4 5b 5 7b", 47 | "composite blues": "1 2 3b 3 4 5b 5 6 7b", 48 | "augmented": "1 2A 3 5 5A 7", 49 | "augmented heptatonic": "1 2A 3 4 5 5A 7", 50 | "dorian #4": "1 2 3b 4# 5 6 7b", 51 | "lydian diminished": "1 2 3b 4# 5 6 7", 52 | "whole tone": "1 2 3 4# 5A 7b", 53 | "leading whole tone": "1 2 3 4# 5A 7b 7", 54 | "harmonic minor": "1 2 3b 4 5 6b 7", 55 | "lydian minor": "1 2 3 4# 5 6b 7b", 56 | "neopolitan": "1 2b 3b 4 5 6b 7", 57 | "neopolitan minor": "1 2b 3b 4 5 6b 7b", 58 | "neopolitan major": "1 2b 3b 4 5 6 7", 59 | "neopolitan major pentatonic": "1 3 4 5b 7b", 60 | "romanian minor": "1 2 3b 5b 5 6 7b", 61 | "double harmonic lydian": "1 2b 3 4# 5 6b 7", 62 | "diminished": "1 2 3b 4 5b 6b 6 7", 63 | "harmonic major": "1 2 3 4 5 6b 7", 64 | "double harmonic major": "1 2b 3 4 5 6b 7", 65 | "egyptian": "1 2 4 5 7b", 66 | "hungarian minor": "1 2 3b 4# 5 6b 7", 67 | "hungarian major": "1 2A 3 4# 5 6 7b", 68 | "oriental": "1 2b 3 4 5b 6 7b", 69 | "spanish": "1 2b 3 4 5 6b 7b", 70 | "spanish heptatonic": "1 2b 3b 3 4 5 6b 7b", 71 | "flamenco": "1 2b 3b 3 4# 5 7b", 72 | "balinese": "1 2b 3b 4 5 6b 7", 73 | "todi raga": "1 2b 3b 4# 5 6b 7", 74 | "malkos raga": "1 3b 4 6b 7b", 75 | "kafi raga": "1 3b 3 4 5 6 7b 7", 76 | "purvi raga": "1 2b 3 4 4# 5 6b 7", 77 | "persian": "1 2b 3 4 5b 6b 7", 78 | "bebop": "1 2 3 4 5 6 7b 7", 79 | "bebop dominant": "1 2 3 4 5 6 7b 7", 80 | "bebop minor": "1 2 3b 3 4 5 6 7b", 81 | "bebop major": "1 2 3 4 5 5A 6 7", 82 | "bebop locrian": "1 2b 3b 4 5b 5 6b 7b", 83 | "minor bebop": "1 2 3b 4 5 6b 7b 7", 84 | "mystery #1": "1 2b 3 5b 6b 7b", 85 | "enigmatic": "1 2b 3 5b 6b 7b 7", 86 | "minor six diminished": "1 2 3b 4 5 6b 6 7", 87 | "ionian augmented": "1 2 3 4 5A 6 7", 88 | "lydian #9": "1 2b 3 4# 5 6 7", 89 | "ichikosucho": "1 2 3 4 5b 5 6 7", 90 | "six tone symmetric": "1 2b 3 4 5A 6" 91 | } 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # music-scale 2 | 3 | [![Build Status](https://travis-ci.org/danigb/music-scale.svg?branch=master)](https://travis-ci.org/danigb/music-scale) 4 | [![Code Climate](https://codeclimate.com/github/danigb/music-scale/badges/gpa.svg)](https://codeclimate.com/github/danigb/music-scale) 5 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/feross/standard) 6 | [![npm version](https://img.shields.io/npm/v/music-scale.svg)](https://www.npmjs.com/package/music-scale) 7 | [![license](https://img.shields.io/npm/l/music-scale.svg)](https://www.npmjs.com/package/music-scale) 8 | [![pitch-array](https://img.shields.io/badge/pitch--array-compatible-yellow.svg)](https://github.com/danigb/pitch-array-notation) 9 | 10 | Music scales made easy: 11 | 12 | ```js 13 | var scale = require('music-scale') 14 | var major = scale('1 2 3 4 5 6 7') 15 | major('A') // => ['A', 'B', 'C#', 'D', 'E', 'F#', 'G#'] 16 | ``` 17 | 18 | It includes a dictionary with more than 100 scales: 19 | 20 | ```js 21 | var lydianAug = scale.fromName('lydian augmented') 22 | lydianAug('C') // => ['C', 'D', 'E', 'F#', 'G#', 'A', 'B'] 23 | ``` 24 | 25 | ## Install 26 | 27 | #### Node 28 | 29 | Install via npm: `npm i --save music-scale` and require it. 30 | 31 | #### Browsers 32 | 33 | Currently there's no distribution for browsers, but is planned. You can use browserify, webpack or a similar tool to create one. 34 | 35 | _For webpack users: If you use the .json files (or any function that consumes the json file like fromName and names) you will need add a plugin to webpack.config file._ 36 | 37 | 38 | ## Usage 39 | 40 | #### Build scales from intervals 41 | 42 | The simplest use case is build scales from intervals: 43 | 44 | ```js 45 | scale('1M 2M 3m 7m', 'F') // => ['F', 'G', 'Ab', 'Eb'] 46 | scale('1 2 3 4 5', 'A3') // => ['A3', 'B3', 'C#4', 'D4', 'E4'] 47 | ``` 48 | 49 | To know the interval string format see [interval-parser](https://github.com/danigb/interval-parser). Notice that if the tonic contains octave, the scale will have octaves in it. 50 | 51 | Also, you can partially apply the `scale` function: 52 | 53 | ```js 54 | var pentatonic = scale('1 2 3 5 6') 55 | pentatonic('E') // => ['E', 'F#', 'G#', 'B', 'C#'] 56 | ``` 57 | 58 | #### Build scales from notes 59 | 60 | You can also use notes as the source of your scale: 61 | 62 | ```js 63 | var lydian = scale('C D E F# G A B') 64 | lydian('A') // => ['A', 'B', 'C#', 'D#', 'E', 'F#', 'G#'] 65 | ``` 66 | 67 | `scale` function assumes that the first note is the tonic. 68 | 69 | #### Get scale intervals 70 | 71 | You can get the scale intervals passing `null` as tonic: 72 | 73 | ```js 74 | var dorian = scale('D E F G A B C') 75 | dorian(null) // => ['1P', '2M', '3m', '4P', '5P', '6M', '7m'] 76 | ``` 77 | 78 | #### Create a dictionary of scales 79 | 80 | You can create a dictionary of scales with a hash that maps names to intervals (or notes). Optionally, you can pass a dictionary of aliases: 81 | 82 | ```js 83 | var scales = scale.fromName( 84 | {'major': 'C D E F G A B', 'minor': 'A B C D E F G'}, 85 | {'ionian': 'major', 'eolian': 'minor'}) 86 | scales('major', 'F') // => ['F', 'G', 'A', 'Bb', 'C', 'D', 'E'] 87 | scales('ionian', 'A') // same as scales('major', 'A') 88 | scales('eolian', 'G') // same as scales('minor', 'G') 89 | scale('dorian', 'C') // => null 90 | ``` 91 | 92 | #### Use the built-in scale dictionary 93 | 94 | `music-scale` has a couple of .json data hash with about 100 scales (see [dict](https://github.com/danigb/music-scale/tree/master/dict) directory): 95 | 96 | ```js 97 | scale.fromName('bebop locrian', 'C') // => [ 'C', 'Db', 'Eb', 'F', 'Gb', 'G', 'Ab', 'Bb'] 98 | ``` 99 | 100 | You can get all scales names with `names` function: 101 | 102 | ```js 103 | scale.names() // => ['major', 'minor', ...] 104 | ``` 105 | 106 | #### Get degrees of a scale 107 | 108 | You can get specific degrees of a scale: 109 | 110 | ```js 111 | scale.degrees('1 3 5', 'C D E F G A B') // => [ 'C', 'E', 'G' ] 112 | scale.degrees('1 5 2 6', 'C D E F G A B') // => [ 'C', 'G', 'D', 'A' ] 113 | ``` 114 | 115 | Notice that scale notes are the same that the order of the degrees 116 | (Jerry Bergonzi would be very happy with this...) 117 | 118 | #### Scale detection 119 | 120 | Not implemented yet: get the name of scale from a collection of notes 121 | 122 | #### This library is too big 123 | 124 | You can require the individual functions: 125 | 126 | ```js 127 | var scale = require('music-scale/scale') 128 | var fromName = require('music-scale/fromName') 129 | ``` 130 | 131 | ### This library is too small 132 | 133 | Sugestions welcomed. Pull request are perfect. 134 | 135 | ## API 136 | 137 | 138 | 139 | 140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |

degrees(degrees, notes) → {Array}

151 |
152 |
153 |
154 |

Get the degrees of a scale

155 |

The resulting array will contain the notes in the same order as degrees. 156 | If a given degree is not present in the scale, the result will contain a 157 | null in that position.

158 |
159 |
Parameters:
160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 176 | 177 | 178 | 179 | 180 | 185 | 186 | 187 | 188 |
NameTypeDescription
degrees 172 | Array 173 | | 174 | String 175 |

the degrees numbers

notes 181 | Array 182 | | 183 | String 184 |

the scale notes

189 |
190 |
Source:
191 |
198 |
199 |
Returns:
200 |
201 |

the notes of the given degrees (or null if not present)

202 |
203 |
204 |
205 | Type 206 |
207 |
208 | Array 209 |
210 |
211 |
Example
212 |
scale.degrees('1 3 5', 'C D E F G A B') // => [ 'C', 'E', 'G' ]
213 | scale.degrees('1 5 2 6', 'C D E F G A B') // => [ 'C', 'G', 'D', 'A' ]
214 | scale.degrees('1 2 6', 'C D E F G') // => [ 'C', 'D', null ]
215 |
216 |
217 |

dictionary(data, aliases) → {function}

218 |
219 |
220 |
221 |

Create a scale builder function from a hash of data

222 |

A scale builder is a function that given a names and a tonic, returns 223 | a scale (array). It can be partially applied.

224 |
225 |
Parameters:
226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 240 | 241 | 242 | 243 | 244 | 247 | 248 | 249 | 250 |
NameTypeDescription
data 238 | Hash 239 |

the data (maps names to intervals or notes)

aliases 245 | Hash 246 |

(Optional) maps names to names in the data hash

251 |
252 |
Source:
253 |
260 |
261 |
Returns:
262 |
263 |

a function to create scales

264 |
265 |
266 |
267 | Type 268 |
269 |
270 | function 271 |
272 |
273 |
Example
274 |
var scales = dictionary({ major: '1 2 3 4 5 6 7', minor: '1 2 3b 4 5 6b 7b' }, {eolian: 'minor'})
275 | scales('major', 'C') // => ['C', 'D', 'E', 'F', 'G', 'A', 'B']
276 | scales('aeolian', 'A') // => ['A', 'B', 'C', 'D', 'E', 'F', 'G']
277 | var minor = scales('minor')
278 | minor('D') // => ['D', 'E', 'F', 'G', 'A', 'Bb', 'C']
279 |
280 |
281 |

fromName(name, tonic)

282 |
283 |
284 |
285 |

Build a scale using a name and a tonic

286 |

It uses a dictionary of scales (see dict directory)

287 |

Can be partially applied (see example)

288 |
289 |
Parameters:
290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 304 | 305 | 306 | 307 | 308 | 313 | 314 | 315 | 316 |
NameTypeDescription
name 302 | String 303 |

the name of the scale

tonic 309 | String 310 | | 311 | Array 312 |

the tonic of the scale

317 |
318 |
Source:
319 |
326 |
327 |
Example
328 |
scale.fromName('bebop locrian', 'C') // => [ 'C', 'Db', 'Eb', 'F', 'Gb', 'G', 'Ab', 'Bb' ]
329 | var kumoi = scale.fromName('kumoi')
330 | kumoi('G') // => ['G', 'A', 'Bb', 'D', 'E']
331 |
332 |
333 |

names(withAliases) → {Array.<String>}

334 |
335 |
336 |
337 |

Get all scale names available

338 |
339 |
Parameters:
340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 354 | 355 | 356 | 357 |
NameTypeDescription
withAliases 352 | Boolean 353 |

set to true to get aliases names

358 |
359 |
Source:
360 |
367 |
368 |
Returns:
369 |
370 |

the list of all scale names

371 |
372 |
373 |
374 | Type 375 |
376 |
377 | Array.<String> 378 |
379 |
380 |
Example
381 |
scale.names() // => ['major', 'minor', ...]
382 |
383 |
384 |

scale(source, tonic) → {Array}

385 |
386 |
387 |
388 |

Build a scale from a source and a tonic. A scale is an array of notes (or 389 | intervals if tonic is null) ordered by frequency

390 |

A source can be a list of intervals or notes. The tonic must be 391 | a pitch (with or without octave) or null to get the scale intervals

392 |

This function is currified, so you can partially apply the function passing 393 | one parameter instead of two (see example)

394 |
395 |
Parameters:
396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 410 | 411 | 412 | 413 | 414 | 417 | 418 | 419 | 420 |
NameTypeDescription
source 408 | Array 409 |

the list of intervals or notes

tonic 415 | String 416 |

the tonic of the scale

421 |
422 |
Source:
423 |
430 |
431 |
Returns:
432 |
433 |

the list of notes

434 |
435 |
436 |
437 | Type 438 |
439 |
440 | Array 441 |
442 |
443 |
Example
444 |
scale('1 2 3 5 6', 'G') // => ['G', 'A', 'B', 'D', 'E']
445 | var dorian = scale('D E F G A B C')
446 | dorian('C4')
447 |
448 |
449 |
450 |
451 |
452 | 453 | *generated with [docme](https://github.com/thlorenz/docme)* 454 |
455 | 456 | 457 | ## License 458 | 459 | MIT License 460 | --------------------------------------------------------------------------------