diff --git a/README.md b/README.md index ffaf2cb2..be3ba26d 100644 --- a/README.md +++ b/README.md @@ -14,19 +14,21 @@ It uses a functional programing style: all functions are pure, there is no data ## Example ```js -import { Note, Interval } from "@tonaljs/tonal"; +import { Note, Interval, Scale } from "@tonaljs/tonal"; -Note.note("A4").midi; // => 60 -Note.note("a4").freq; // => 440 -Note.note("c#2").accidentals; // => '#' -Note.note("x").midi; // => undefined +Note.midi("A4"); // => 60 +Note.freq("a4").freq; // => 440 +Note.accidentals("c#2"); // => '#' Note.transpose("C4", "5P"); // => "G4" -Interval.interval("5P").semitones; // => 7 +Interval.semitones("5P"); // => 7 Interval.distance("C4", "G4"); // => "5P" +Scale.get("C major").notes; // =>["C", "D", "E", "F", "G", "A", "B"]; ``` ## Install +Install all packages at once: + ```bash npm install --save @tonaljs/tonal ``` @@ -60,37 +62,53 @@ Grab the [minified browser ready version](https://raw.githubusercontent.com/tona #### Bundle size -`@tonaljs/tonal` includes all published modules. Although tonal it is small, you can reduce bundle sizes by importing the modules individually, or even only the functions you need: +`@tonaljs/tonal` includes all published modules. -``` -npm i @tonaljs/core +Although the final bundle it is small (less than 9kb minified and gzipped), you can reduce bundle sizes by installing the modules individually, and importing the functions you need: + +```bash +npm i @tonaljs/note ``` ```js -import { transpose } from "@tonaljs/core"; +import { transpose } from "@tonaljs/note"; transpose("A4", "P5"); ``` ## Documentation -The API documentation lives inside README.md file of each module: +Generally, you just need to install: - [@tonaljs/tonal](/packages/tonal): All modules bundled in one package + +The API documentation lives inside README.md file of each module + +#### Notes and intervals + - [@tonaljs/note](/packages/note): Note operations (simplify, transposeBy ) - [@tonaljs/midi](/packages/midi): Midi number conversions -- [@tonaljs/scale](/packages/scale): Scales and its relations -- [@tonaljs/chord](/packages/chord): Chords and its relations - [@tonaljs/interval](/packages/interval): Interval operations (add, simplify, invert) -- [@tonaljs/pcset](/packages/pcset): Pitch class sets properties -- [@tonaljs/mode](/packages/mode): Parse (greek) tonal modes (ionian, dorian, ...) -- [@tonaljs/scale-dictionary](/packages/scale-dictionary): A dictionary of scales -- [@tonaljs/chord-dictionary](/packages/chord-dictionary): A dictionary of chords -- [@tonaljs/key](/packages/key): Major and minor keys scales and chords +- [@tonaljs/abc-notation](/packages/abc-notation): Parse ABC notation notes + +#### Scales and chords + +- [@tonaljs/scale](/packages/scale): Scales +- [@tonaljs/chord](/packages/chord): Chords +- [@tonaljs/scale-type](/packages/scale-type): A dictionary of scales +- [@tonaljs/chord-type](/packages/chord-type): A dictionary of chords +- [@tonaljs/pcset](/packages/pcset): Pitch class sets. Compare note groups. +- [@tonaljs/mode](/packages/mode): A dictionary of Greek modes (ionian, dorian...) + +#### Keys, chord progressions + +- [@tonaljs/key](/packages/key): Major and minor keys, it's scales and chords - [@tonaljs/progression](/packages/progression): Chord progressions - [@tonaljs/roman-numeral](/packages/roman-numeral): Parse roman numeral symbols -- [@tonaljs/abc-notation](/packages/abc-notation): Parse ABC notation notes + +#### Utilities + - [@tonaljs/core](/packages/core): Core functions (note, interval, transpose and distance) -- [@tonaljs/array](/packages/array): Array manipulation +- [@tonaljs/collection](/packages/collection): Utility functions to work with collections (range, shuffle, permutations) - [@tonaljs/range](/packages/range): Create note ranges ## Contributing @@ -106,6 +124,7 @@ This library takes inspiration from other music theory libraries: - MusicKit: https://github.com/benzguo/MusicKit - Music21: http://web.mit.edu/music21/doc/index.html - Sharp11: https://github.com/jsrmath/sharp11 +- python-mingus: https://github.com/bspaans/python-mingus ## Projects using tonal diff --git a/docs/migration-guide.md b/docs/migration-guide.md index 5c51f887..34a479a3 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -2,13 +2,13 @@ ## From version 3 to 4 -### `chord-dictionary` +### `chord-type` -- `ChordDictionary.chordType` renamed to `ChordDictionary.get` +- `ChordType.chordType` renamed to `ChordType.get` -### `scale-dictionary` +### `scale-type` -- `ScaleDictionary.scaleType` renamed to `ScaleDictionary.get` +- `ScaleType.scaleType` renamed to `ScaleType.get` ## From version 2 to 3 diff --git a/packages/abc-notation/README.md b/packages/abc-notation/README.md index 93b6a7b1..0851b2d0 100644 --- a/packages/abc-notation/README.md +++ b/packages/abc-notation/README.md @@ -2,37 +2,50 @@ > Convert note names between scientific and abc notation -## References +## Usage + +ES6: -- - [ABC Notation](https://en.wikipedia.org/wiki/ABC_notation) +```js +import { AbcNotation } from "@tonaljs/tonal"; +``` + +nodejs: + +```js +const { AbcNotation } = require("@tonaljs/tonal"); +``` ## API #### `abcToScientificNotation(noteNameInAbc: string) => string` ```js -abcToScientificNotation("c"); // => "C5" +AbcNotation.abcToScientificNotation("c"); // => "C5" ``` #### `scientificToAbcNotation(noteNameInScientific: string) => string` ```js -scientificToAbcNotation("C#4"); // => "^C" +AbcNotation.scientificToAbcNotation("C#4"); // => "^C" ``` -## How to? +#### `transpose(note: string, interval: string) => string` -#### Transpose notes in abc notation +Transpose an note in abc notation: ```js -import { transpose } from "@tonaljs/note"; -import { - abcToScientificNotation, - scientificToAbcNotation -} from "@tonal/abc-notation"; +AbcNotation.transpose("=C", "P19"); // => "g'" +``` -const transposeAbc = (note, interval) => - scientificToAbcNotation(abcToScientificNotation(note), interval); +#### `distance(from: string, to: string) => string` -transposeAbc("c", "P5"); +Find the interval between two notes in abc notation: + +```js +AbcNotation.distance("=C", "g"); // => "12P" ``` + +## References + +- [ABC Notation](https://en.wikipedia.org/wiki/ABC_notation) diff --git a/packages/abc-notation/abc-notation.test.ts b/packages/abc-notation/abc-notation.test.ts index 69f4bc1e..7f7ca570 100644 --- a/packages/abc-notation/abc-notation.test.ts +++ b/packages/abc-notation/abc-notation.test.ts @@ -1,32 +1,30 @@ -import { - abcToScientificNotation, - scientificToAbcNotation, - tokenize, - transpose -} from "./index"; +import AbcNotation from "./index"; describe("@tonaljs/abc-notation", () => { test("tokenize", () => { - expect(tokenize("C,',")).toEqual(["", "C", ",',"]); - expect(tokenize("g,,'")).toEqual(["", "g", ",,'"]); - expect(tokenize("")).toEqual(["", "", ""]); - expect(tokenize("m")).toEqual(["", "", ""]); - expect(tokenize("c#")).toEqual(["", "", ""]); + expect(AbcNotation.tokenize("C,',")).toEqual(["", "C", ",',"]); + expect(AbcNotation.tokenize("g,,'")).toEqual(["", "g", ",,'"]); + expect(AbcNotation.tokenize("")).toEqual(["", "", ""]); + expect(AbcNotation.tokenize("m")).toEqual(["", "", ""]); + expect(AbcNotation.tokenize("c#")).toEqual(["", "", ""]); }); test("transpose", () => { - expect(transpose("=C", "P19")).toEqual("g'"); + expect(AbcNotation.transpose("=C", "P19")).toEqual("g'"); + }); + test("distance", () => { + expect(AbcNotation.distance("=C", "g")).toEqual("12P"); }); test("toNote", () => { const ABC = ["__A,,", "_B,", "=C", "d", "^e'", "^^f''", "G,,''", "g,,,'''"]; const SCIENTIFIC = ["Abb2", "Bb3", "C4", "D5", "E#6", "F##7", "G4", "G5"]; - expect(ABC.map(abcToScientificNotation)).toEqual(SCIENTIFIC); + expect(ABC.map(AbcNotation.abcToScientificNotation)).toEqual(SCIENTIFIC); }); test("toAbc", () => { const SCIENTIFIC = ["Abb2", "Bb3", "C4", "D5", "E#6", "F##7", "G#2", "Gb7"]; const ABC = ["__A,,", "_B,", "C", "d", "^e'", "^^f''", "^G,,", "_g''"]; - expect(SCIENTIFIC.map(scientificToAbcNotation)).toEqual(ABC); + expect(SCIENTIFIC.map(AbcNotation.scientificToAbcNotation)).toEqual(ABC); }); }); diff --git a/packages/abc-notation/index.ts b/packages/abc-notation/index.ts index 7af06cee..32454d82 100644 --- a/packages/abc-notation/index.ts +++ b/packages/abc-notation/index.ts @@ -1,4 +1,4 @@ -import { note, transpose as tr } from "@tonaljs/core"; +import { distance as dist, note, transpose as tr } from "@tonaljs/core"; const fillStr = (character: string, times: number) => Array(times + 1).join(character); @@ -63,3 +63,15 @@ export function scientificToAbcNotation(str: string): string { export function transpose(note: string, interval: string): string { return scientificToAbcNotation(tr(abcToScientificNotation(note), interval)); } + +export function distance(from: string, to: string): string { + return dist(abcToScientificNotation(from), abcToScientificNotation(to)); +} + +export default { + abcToScientificNotation, + scientificToAbcNotation, + tokenize, + transpose, + distance +}; diff --git a/packages/array/README.md b/packages/array/README.md index 58b22438..9256cce9 100644 --- a/packages/array/README.md +++ b/packages/array/README.md @@ -1,59 +1,3 @@ -# @tonaljs/array ![tonal](https://img.shields.io/badge/@tonaljs-array-yellow.svg?style=flat-square) [![npm version](https://img.shields.io/npm/v/@tonaljs/array.svg?style=flat-square)](https://www.npmjs.com/package/@tonaljs/array) +# deprecated: @tonaljs/array -> A collection of functions to create and manipulate arrays of notes or intervals. - -## API - -### `range(from: number, to: number) => number[]` - -Creates a numeric range: - -```js -range(-2, 2); // => [-2, -1, 0, 1, 2] -range(2, -2); // => [2, 1, 0, -1, -2] -``` - -### `rotate(times: number, array: any[]) => any[]` - -Rotate an array a number of times: - -```js -rotate(1, [1, 2, 3]); // => [2, 3, 1] -``` - -### `sortedNoteNames(array: any[]) => string[]` - -Sort an array of note names in ascending order. Pitch classes are listed before notes. -Any string that is not a note is removed. - -```js -sortedNoteNames(["c2", "c5", "c1", "c0", "c6", "c"]); -// => ['C', 'C0', 'C1', 'C2', 'C5', 'C6'] -sortedNoteNames(["c", "F", "G", "a", "b", "h", "J"]); -// => ['C', 'F', 'G', 'A', 'B'] -``` - -### `sortedUniqNoteNames(array: any[]) => string[]` - -Return a list of sorted note names with duplications removed. - -### `shuffle(array: any[]) => any[]` - -Randomizes the order of the specified array in-place, using the Fisher–Yates shuffle. - -### `permutations(array: any[]) => any[][]` - -Get all permutations of an array - -```js -permutations(["a", "b", "c"])) // => -// => -// [ -// ["a", "b", "c"], -// ["b", "a", "c"], -// ["b", "c", "a"], -// ["a", "c", "b"], -// ["c", "a", "b"], -// ["c", "b", "a"] -// ] -``` +Renamed to [`@tonaljs/collection`](/packages/collection) diff --git a/packages/chord-dictionary/LICENSE b/packages/chord-detect/LICENSE similarity index 100% rename from packages/chord-dictionary/LICENSE rename to packages/chord-detect/LICENSE diff --git a/packages/chord-detect/README.md b/packages/chord-detect/README.md new file mode 100644 index 00000000..c6946678 --- /dev/null +++ b/packages/chord-detect/README.md @@ -0,0 +1,34 @@ +# @tonaljs/chord-detect ![tonal](https://img.shields.io/badge/@tonaljs-chord-detect-yellow.svg?style=flat-square) [![npm version](https://img.shields.io/npm/v/@tonaljs/chord-detect.svg?style=flat-square)](https://www.npmjs.com/package/@tonaljs/chord-detect) + +## Usage + +With ES6 `import`: + +```js +import { Chord } from "@tonaljs/tonal"; +``` + +With ES5 `require`: + +```js +const { Chord } = require("@tonaljs/tonal"); +``` + +Standalone: + +```js +import { detect } from "@tonaljs/chord-detect"; +``` + +## API + +### `Chord.detect(notes: string[]) => string[]` + +Examples: + +```js +Chord.detect(["D", "F#", "A", "C"]); // => ["D7"] +Chord.detect(["F#", "A", "C", "D"]); // => ["D7/F#"] +Chord.detect(["A", "C", "D", "F#"]); // => ["D7/A"] +Chord.detect(["E", "G#", "B", "C#"]); // => ["E6", "C#m7/E"] +``` diff --git a/packages/chord-detect/chord-detect.test.ts b/packages/chord-detect/chord-detect.test.ts new file mode 100644 index 00000000..56f973ef --- /dev/null +++ b/packages/chord-detect/chord-detect.test.ts @@ -0,0 +1,15 @@ +// tslint:disable-next-line: no-implicit-dependencies +import { detect } from "./index"; + +describe("@tonal/chord-detect", () => { + test("detect", () => { + expect(detect(["D", "F#", "A", "C"])).toEqual(["D7"]); + expect(detect(["F#", "A", "C", "D"])).toEqual(["D7/F#"]); + expect(detect(["A", "C", "D", "F#"])).toEqual(["D7/A"]); + expect(detect(["E", "G#", "B", "C#"])).toEqual(["E6", "C#m7/E"]); + }); + + test("edge cases", () => { + expect(detect([])).toEqual([]); + }); +}); diff --git a/packages/chord-detect/index.ts b/packages/chord-detect/index.ts new file mode 100644 index 00000000..f73a0089 --- /dev/null +++ b/packages/chord-detect/index.ts @@ -0,0 +1,60 @@ +import { get as chordType } from "@tonaljs/chord-type"; +import { note } from "@tonaljs/core"; +import { modes } from "@tonaljs/pcset"; + +interface FoundChord { + readonly weight: number; + readonly name: string; +} + +const NotFound: FoundChord = { weight: 0, name: "" }; + +const namedSet = (notes: string[]) => { + const pcToName = notes.reduce>((record, n) => { + const chroma = note(n).chroma; + if (chroma !== undefined) { + record[chroma] = record[chroma] || note(n).name; + } + return record; + }, {}); + + return (chroma: number) => pcToName[chroma]; +}; + +export function detect(source: string[]): string[] { + const notes = source.map(n => note(n).pc).filter(x => x); + if (note.length === 0) { + return []; + } + + const found: FoundChord[] = findExactMatches(notes, 1); + + return found + .filter(chord => chord.weight) + .sort((a, b) => b.weight - a.weight) + .map(chord => chord.name); +} + +function findExactMatches(notes: string[], weight: number): FoundChord[] { + const tonic = notes[0]; + const tonicChroma = note(tonic).chroma; + const noteName = namedSet(notes); + const allModes = modes(notes, false); + + const found: FoundChord[] = allModes.map((mode, chroma) => { + const chordName = chordType(mode).aliases[0]; + if (!chordName) { + return NotFound; + } + const baseNote = noteName(chroma); + const isInversion = chroma !== tonicChroma; + if (isInversion) { + return { weight: 0.5 * weight, name: `${baseNote}${chordName}/${tonic}` }; + } else { + return { weight: 1 * weight, name: `${baseNote}${chordName}` }; + } + }); + return found; +} + +export default { detect }; diff --git a/packages/chord-detect/package.json b/packages/chord-detect/package.json new file mode 100644 index 00000000..f8b9960d --- /dev/null +++ b/packages/chord-detect/package.json @@ -0,0 +1,28 @@ +{ + "name": "@tonaljs/chord-detect", + "version": "3.4.5", + "description": "Detect chord name based on note names", + "keywords": [ + "chord-detect", + "music", + "theory", + "@tonaljs/tonal" + ], + "main": "dist/index.es5.js", + "module": "dist/index.esnext.js", + "files": [ + "dist" + ], + "types": "dist/chord-detect/index.d.ts", + "dependencies": { + "@tonaljs/chord-type": "^3.3.1", + "@tonaljs/pcset": "^3.2.3", + "@tonaljs/core": "^3.2.2" + }, + "devDependencies": {}, + "author": "danigb@gmail.com", + "license": "MIT", + "publishConfig": { + "access": "public" + } +} diff --git a/packages/chord-dictionary/README.md b/packages/chord-dictionary/README.md index 657ad5c0..394fdd49 100644 --- a/packages/chord-dictionary/README.md +++ b/packages/chord-dictionary/README.md @@ -1,88 +1 @@ -# @tonaljs/chord-dictionary ![tonal](https://img.shields.io/badge/@tonaljs-chord_dictionary-yellow.svg?style=flat-square) [![npm version](https://img.shields.io/npm/v/@tonaljs/chord-dictionary.svg?style=flat-square)](https://www.npmjs.com/package/@tonaljs/chord-dictionary) - -> A dictionary of musical chords. - -## Usage - -```js -import { get } from "@tonaljs/chord-dictionary"; -// or -const { get } = require("@tonaljs/chord-dictionary"); -``` - -## API - -#### `get(name: string) => ChordType` - -Given a chord type name, return a ChordType object with the following properties: - -- name: the chord type name -- aliases: a list of alternative names -- quality: Major | Minor | Augmented | Diminished | Unknown -- num: the pcset number -- chroma: the pcset chroma -- length: the number of notes -- intervals: the interval list - -Example: - -```js -get("major"); // => -// { -// name: "major", -// aliases: ["M", ""], -// quality: "Major", -// intervals: ["1P", "3M", "5P"], -// num: 2192, -// chroma: "100010010000", -// length: 3 -// }); -``` - -#### `entries() => ChordType[]` - -Return a list of all available chord types. - -#### `add(intervals: string[], names: string[], fullName?: string) => ChordType` - -Add a chord type to dictionary: - -```js -add(["1P", "3M", "5P"], ["M"], "mayor"); -``` - -## HOW TO - -#### Get all chord names - -You can get (long) chord names: - -```js -entries() - .map(type => type.name) - .filter(name => name); -``` - -Or the first (short) name: - -```js -entries() - .map(type => type.aliases[0]) - .filter(name => name); -``` - -#### How to get triad chord names? - -```js -entries() - .filter(get => get.length === 3) - .map(get => get.name); -``` - -#### How to add a chord type to the dictionary? - -```js -add(["1P", "3M", "5P"], ["M", "may"], "mayor"); -get("mayor"); // => { name: 'mayor', quality: "Major", chroma: ... } -get("may"); // => { name: 'mayor', quality: "Major", chroma: ... } -``` +# Deprecated. Use [@tonaljs/chord-type](https://github.com/tonaljs/tonal/tree/master/packages/chord-type) diff --git a/packages/chord-dictionary/chord-dictionary.test.ts b/packages/chord-dictionary/chord-dictionary.test.ts deleted file mode 100644 index 0677f042..00000000 --- a/packages/chord-dictionary/chord-dictionary.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { add, clear, entries, get, keys } from "./index"; - -const $ = (str: string) => str.split(" "); - -describe("@tonaljs/chord-dictionary", () => { - test("list names", () => { - expect(entries()).toHaveLength(108); - // sorted - expect(entries()[0].name).toEqual("fifth"); - }); - - test("get ", () => { - expect(get("major")).toEqual({ - empty: false, - setNum: 2192, - name: "major", - quality: "Major", - intervals: ["1P", "3M", "5P"], - aliases: ["M", ""], - chroma: "100010010000", - normalized: "100001000100" - }); - }); - - test("add a chord", () => { - add(["1P", "5P"], ["q"]); - expect(get("q")).toMatchObject({ - chroma: "100000010000" - }); - add(["1P", "5P"], ["q"], "quinta"); - expect(get("quinta")).toEqual(get("q")); - }); - - test("clear dictionary", () => { - clear(); - expect(entries()).toEqual([]); - expect(keys()).toEqual([]); - }); -}); diff --git a/packages/chord-dictionary/index.ts b/packages/chord-dictionary/index.ts index be05075f..d19d7edf 100644 --- a/packages/chord-dictionary/index.ts +++ b/packages/chord-dictionary/index.ts @@ -1,124 +1 @@ -import { - EmptyPcset, - Pcset, - pcset, - PcsetChroma, - PcsetNum -} from "@tonaljs/pcset"; -import data from "./data"; - -export type ChordQuality = - | "Major" - | "Minor" - | "Augmented" - | "Diminished" - | "Unknown"; - -export interface ChordType extends Pcset { - name: string; - quality: ChordQuality; - aliases: string[]; -} -const NoChordType: ChordType = { - ...EmptyPcset, - name: "", - quality: "Unknown", - intervals: [], - aliases: [] -}; - -type ChordTypeName = string | PcsetChroma | PcsetNum; - -let chords: ChordType[] = []; -let index: Record = {}; - -/** - * Given a chord name or chroma, return the chord properties - * @param {string} source - chord name or pitch class set chroma - * @example - * import { get } from 'tonaljs/chord-dictionary' - * get('major') // => { name: 'major', ... } - */ -export function get(type: ChordTypeName): ChordType { - return index[type] || NoChordType; -} - -/** - * @deprecated - * @see get - */ -export function chordType(type: ChordTypeName): ChordType { - // tslint:disable-next-line - console.warn( - "ChordDictionary.chordType is deprecated. Use ChordDictionary.get instead" - ); - return get(type); -} - -/** - * Keys used to reference chord types - */ -export function keys() { - return Object.keys(index); -} - -/** - * Return a list of all chord types - */ -export function entries(): ChordType[] { - return chords.slice(); -} - -/** - * Clear the dictionary - */ -export function clear() { - chords = []; - index = {}; -} - -/** - * Add a chord to the dictionary. - * @param intervals - * @param aliases - * @param [fullName] - */ -export function add(intervals: string[], aliases: string[], fullName?: string) { - const quality = getQuality(intervals); - const chord = { - ...pcset(intervals), - name: fullName || "", - quality, - intervals, - aliases - }; - chords.push(chord); - if (chord.name) { - index[chord.name] = chord; - } - index[chord.setNum] = chord; - index[chord.chroma] = chord; - chord.aliases.forEach(alias => addAlias(chord, alias)); -} - -export function addAlias(chord: ChordType, alias: string) { - index[alias] = chord; -} - -function getQuality(intervals: string[]): ChordQuality { - const has = (interval: string) => intervals.indexOf(interval) !== -1; - return has("5A") - ? "Augmented" - : has("3M") - ? "Major" - : has("5d") - ? "Diminished" - : has("3m") - ? "Minor" - : "Unknown"; -} - -data.forEach(([ivls, fullName, names]: string[]) => - add(ivls.split(" "), names.split(" "), fullName) -); -chords.sort((a, b) => a.setNum - b.setNum); +export * from "@tonaljs/chord-type"; diff --git a/packages/chord-dictionary/package.json b/packages/chord-dictionary/package.json index 4b345b0c..a5ab933f 100644 --- a/packages/chord-dictionary/package.json +++ b/packages/chord-dictionary/package.json @@ -2,14 +2,7 @@ "name": "@tonaljs/chord-dictionary", "version": "3.4.1", "description": "A dictionary of musical chords", - "keywords": [ - "chord", - "dictionary", - "chords", - "music", - "theory", - "@tonaljs/core" - ], + "keywords": [], "main": "dist/index.es5.js", "module": "dist/index.esnext.js", "files": [ @@ -17,8 +10,7 @@ ], "types": "dist/chord-dictionary/index.d.ts", "dependencies": { - "@tonaljs/interval": "^3.2.4", - "@tonaljs/pcset": "^3.2.4" + "@tonaljs/chord-type": "^3.4.1" }, "author": "danigb@gmail.com", "license": "MIT", diff --git a/packages/scale-dictionary/LICENSE b/packages/chord-type/LICENSE similarity index 100% rename from packages/scale-dictionary/LICENSE rename to packages/chord-type/LICENSE diff --git a/packages/chord-type/README.md b/packages/chord-type/README.md new file mode 100644 index 00000000..c810afd5 --- /dev/null +++ b/packages/chord-type/README.md @@ -0,0 +1,84 @@ +# @tonaljs/chord-type ![tonal](https://img.shields.io/badge/@tonaljs-chord_dictionary-yellow.svg?style=flat-square) [![npm version](https://img.shields.io/npm/v/@tonaljs/chord-type.svg?style=flat-square)](https://www.npmjs.com/package/@tonaljs/chord-type) + +> A dictionary of musical chords. + +## Usage + +ES6: + +```js +import { ChordType } from "@tonaljs/tonal"; +``` + +node: + +```js +const { ChordType } = require("@tonaljs/tonal"); +``` + +## API + +#### `ChordType.get(name: string) => object` + +Given a chord type name, return an object with the following properties: + +- name: the chord type name +- aliases: a list of alternative names +- quality: Major | Minor | Augmented | Diminished | Unknown +- num: the pcset number +- chroma: the pcset chroma +- length: the number of notes +- intervals: the interval list + +Example: + +```js +ChordType.get("major"); // => +// { +// name: "major", +// aliases: ["M", ""], +// quality: "Major", +// intervals: ["1P", "3M", "5P"], +// num: 2192, +// chroma: "100010010000", +// length: 3 +// }); +``` + +#### `ChordType.names() => string[]` + +List all chord type (long) names in the dictionary + +#### `ChordType.symbols() => string[]` + +List all chord type (long) names in the dictionary + +#### `ChordType.all() => object[]` + +Return a list of all available chord types. + +#### `ChordType.add(intervals: string[], names: string[], fullName?: string) => ChordType` + +Add a chord type to dictionary: + +```js +add(["1P", "3M", "5P"], ["M"], "mayor"); +``` + +## HOW TO + +#### How to get triad chord names? + +```js +ChordType.all() + .filter(get => get.length === 3) + .map(get => get.name); +``` + +#### How to add a chord type to the dictionary? + +```js +ChordType.add(["1P", "3M", "5P"], ["M", "may"], "mayor"); +ChordType.get("mayor"); // => { name: 'mayor', quality: "Major", chroma: ... } +ChordType.get("may"); // => { name: 'mayor', quality: "Major", chroma: ... } +``` diff --git a/packages/chord-type/chord-type.test.ts b/packages/chord-type/chord-type.test.ts new file mode 100644 index 00000000..4f61e503 --- /dev/null +++ b/packages/chord-type/chord-type.test.ts @@ -0,0 +1,57 @@ +import ChordType from "./index"; + +const $ = (str: string) => str.split(" "); + +describe("@tonaljs/chord-type", () => { + test("names", () => { + // sorted + expect(ChordType.names().slice(0, 5)).toEqual([ + "fifth", + "suspended 4th", + "suspended 4th seventh", + "augmented", + "major seventh b6" + ]); + }); + + test("symbols", () => { + // sorted + expect(ChordType.symbols().slice(0, 3)).toEqual([ + "5", + "M7#5sus4", + "7#5sus4" + ]); + }); + + test("all", () => { + expect(ChordType.all()).toHaveLength(108); + }); + + test("get ", () => { + expect(ChordType.get("major")).toEqual({ + empty: false, + setNum: 2192, + name: "major", + quality: "Major", + intervals: ["1P", "3M", "5P"], + aliases: ["M", ""], + chroma: "100010010000", + normalized: "100001000100" + }); + }); + + test("add a chord", () => { + ChordType.add(["1P", "5P"], ["q"]); + expect(ChordType.get("q")).toMatchObject({ + chroma: "100000010000" + }); + ChordType.add(["1P", "5P"], ["q"], "quinta"); + expect(ChordType.get("quinta")).toEqual(ChordType.get("q")); + }); + + test("clear dictionary", () => { + ChordType.removeAll(); + expect(ChordType.all()).toEqual([]); + expect(ChordType.keys()).toEqual([]); + }); +}); diff --git a/packages/chord-dictionary/data.ts b/packages/chord-type/data.ts similarity index 100% rename from packages/chord-dictionary/data.ts rename to packages/chord-type/data.ts diff --git a/packages/chord-type/index.ts b/packages/chord-type/index.ts new file mode 100644 index 00000000..9b2e5383 --- /dev/null +++ b/packages/chord-type/index.ts @@ -0,0 +1,144 @@ +import { deprecate } from "@tonaljs/core"; +import { + EmptyPcset, + get as pcset, + Pcset, + PcsetChroma, + PcsetNum +} from "@tonaljs/pcset"; +import data from "./data"; + +export type ChordQuality = + | "Major" + | "Minor" + | "Augmented" + | "Diminished" + | "Unknown"; + +export interface ChordType extends Pcset { + name: string; + quality: ChordQuality; + aliases: string[]; +} +const NoChordType: ChordType = { + ...EmptyPcset, + name: "", + quality: "Unknown", + intervals: [], + aliases: [] +}; + +type ChordTypeName = string | PcsetChroma | PcsetNum; + +let dictionary: ChordType[] = []; +let index: Record = {}; + +/** + * Given a chord name or chroma, return the chord properties + * @param {string} source - chord name or pitch class set chroma + * @example + * import { get } from 'tonaljs/chord-type' + * get('major') // => { name: 'major', ... } + */ +export function get(type: ChordTypeName): ChordType { + return index[type] || NoChordType; +} + +export const chordType = deprecate("ChordType.chordType", "ChordType.get", get); + +/** + * Get all chord (long) names + */ +export function names() { + return dictionary.map(chord => chord.name).filter(x => x); +} + +/** + * Get all chord symbols + */ +export function symbols() { + return dictionary.map(chord => chord.aliases[0]).filter(x => x); +} + +/** + * Keys used to reference chord types + */ +export function keys() { + return Object.keys(index); +} + +/** + * Return a list of all chord types + */ +export function all(): ChordType[] { + return dictionary.slice(); +} + +export const entries = deprecate("ChordType.entries", "ChordType.all", all); + +/** + * Clear the dictionary + */ +export function removeAll() { + dictionary = []; + index = {}; +} + +/** + * Add a chord to the dictionary. + * @param intervals + * @param aliases + * @param [fullName] + */ +export function add(intervals: string[], aliases: string[], fullName?: string) { + const quality = getQuality(intervals); + const chord = { + ...pcset(intervals), + name: fullName || "", + quality, + intervals, + aliases + }; + dictionary.push(chord); + if (chord.name) { + index[chord.name] = chord; + } + index[chord.setNum] = chord; + index[chord.chroma] = chord; + chord.aliases.forEach(alias => addAlias(chord, alias)); +} + +export function addAlias(chord: ChordType, alias: string) { + index[alias] = chord; +} + +function getQuality(intervals: string[]): ChordQuality { + const has = (interval: string) => intervals.indexOf(interval) !== -1; + return has("5A") + ? "Augmented" + : has("3M") + ? "Major" + : has("5d") + ? "Diminished" + : has("3m") + ? "Minor" + : "Unknown"; +} + +data.forEach(([ivls, fullName, names]: string[]) => + add(ivls.split(" "), names.split(" "), fullName) +); +dictionary.sort((a, b) => a.setNum - b.setNum); + +export default { + names, + symbols, + get, + all, + add, + removeAll, + keys, + // deprecated + entries, + chordType +}; diff --git a/packages/chord-type/package.json b/packages/chord-type/package.json new file mode 100644 index 00000000..ee78141b --- /dev/null +++ b/packages/chord-type/package.json @@ -0,0 +1,28 @@ +{ + "name": "@tonaljs/chord-type", + "version": "3.4.1", + "description": "A dictionary of musical chords", + "keywords": [ + "chord", + "dictionary", + "chords", + "music", + "theory", + "@tonaljs/core" + ], + "main": "dist/index.es5.js", + "module": "dist/index.esnext.js", + "files": [ + "dist" + ], + "types": "dist/chord-type/index.d.ts", + "dependencies": { + "@tonaljs/core": "^3.2.4", + "@tonaljs/pcset": "^3.2.4" + }, + "author": "danigb@gmail.com", + "license": "MIT", + "publishConfig": { + "access": "public" + } +} diff --git a/packages/chord/README.md b/packages/chord/README.md index e2a7c336..007ed0c5 100644 --- a/packages/chord/README.md +++ b/packages/chord/README.md @@ -2,21 +2,21 @@ ## Usage -ES6 modules: +ES6: ```js -import { chord } from "@tonaljs/chord"; +import { Chord } from "@tonaljs/tonal"; ``` -Nodejs modules: +Nodejs: ```js -const { chord } = require("@tonaljs/chord"); +const { Chord } = require("@tonaljs/tonal"); ``` ## API -### `chord(name: string) => Scale` +#### `Chord.get(name: string) => Scale` Get a chord from a chord name. Unlike `chordType`, `chord` accepts tonics in the chord name and returns the a Scale data object (a ChordType with extended properties): @@ -24,7 +24,7 @@ Get a chord from a chord name. Unlike `chordType`, `chord` accepts tonics in the - notes: an array of notes, or empty array if tonic is not present ```js -chord("Cmaj7"); +Chord.get("Cmaj7"); // => // { // name: "C major seventh", @@ -39,36 +39,44 @@ chord("Cmaj7"); // }; ``` -### `transpose(chordName: string, intervalName: string) => string` +#### `Chord.detect(notes: string[]) => string[]` + +Given a list of notes, get the possible chord names: + +```js +Chord.detect(["D", "F#", "A", "C"]); // => ["D7"] +``` + +#### `Chord.transpose(chordName: string, intervalName: string) => string` Transpose a chord name by an interval: ```js -transpose("Eb7b9", "5P"); // => "Bb7b9" +Chord.transpose("Eb7b9", "5P"); // => "Bb7b9" ``` -### `chordScales(chordName: string) => string[]` +#### `Chord.chordScales(chordName: string) => string[]` -Get all scales where the given chord fits +Get all scales where the given chord fits: ```js -chordScales("C7b9"); +Chord.chordScales("C7b9"); // => ["phrygian dominant", "flamenco", "spanish heptatonic", "half-whole diminished", "chromatic"] ``` -### `extended(chord: string) => string[]` +#### `Chord.extended(chord: string) => string[]` Get all chords names that are a superset of the given one (has the same notes and at least one more) ```js -extended("Cmaj7"); +Chord.extended("Cmaj7"); // => [ 'Cmaj#4', 'Cmaj7#9#11', 'Cmaj9', 'CM7add13', 'Cmaj13', 'Cmaj9#11', 'CM13#11', 'CM7b9' ] ``` -### `reduced(chord: string) => string[]` +#### `Chord.reduced(chord: string) => string[]` Find all chords names that are a subset of the given one (less notes but all from the given chord) ```js -Scale.reduced("Cmaj7"); // => ["C5", "CM"] +Chord.reduced("Cmaj7"); // => ["C5", "CM"] ``` diff --git a/packages/chord/chord.test.ts b/packages/chord/chord.test.ts index 51dc253e..85e79abc 100644 --- a/packages/chord/chord.test.ts +++ b/packages/chord/chord.test.ts @@ -1,35 +1,28 @@ -import { - chord, - chordScales, - extended, - reduced, - tokenize, - transpose -} from "./index"; +import Chord from "./index"; const $ = (str: string) => str.split(" "); describe("tonal-chord", () => { test("tokenize", () => { - expect(tokenize("Cmaj7")).toEqual(["C", "maj7"]); - expect(tokenize("c7")).toEqual(["C", "7"]); - expect(tokenize("maj7")).toEqual(["", "maj7"]); - expect(tokenize("c#4 m7b5")).toEqual(["C#4", "m7b5"]); - expect(tokenize("c#4m7b5")).toEqual(["C#4", "m7b5"]); - expect(tokenize("Cb7b5")).toEqual(["Cb", "7b5"]); - expect(tokenize("Eb7add6")).toEqual(["Eb", "7add6"]); - expect(tokenize("Bb6b5")).toEqual(["Bb", "6b5"]); - expect(tokenize("aug")).toEqual(["", "aug"]); - expect(tokenize("C11")).toEqual(["C", "11"]); - expect(tokenize("C13no5")).toEqual(["C", "13no5"]); - expect(tokenize("C64")).toEqual(["C", "64"]); + expect(Chord.tokenize("Cmaj7")).toEqual(["C", "maj7"]); + expect(Chord.tokenize("c7")).toEqual(["C", "7"]); + expect(Chord.tokenize("maj7")).toEqual(["", "maj7"]); + expect(Chord.tokenize("c#4 m7b5")).toEqual(["C#4", "m7b5"]); + expect(Chord.tokenize("c#4m7b5")).toEqual(["C#4", "m7b5"]); + expect(Chord.tokenize("Cb7b5")).toEqual(["Cb", "7b5"]); + expect(Chord.tokenize("Eb7add6")).toEqual(["Eb", "7add6"]); + expect(Chord.tokenize("Bb6b5")).toEqual(["Bb", "6b5"]); + expect(Chord.tokenize("aug")).toEqual(["", "aug"]); + expect(Chord.tokenize("C11")).toEqual(["C", "11"]); + expect(Chord.tokenize("C13no5")).toEqual(["C", "13no5"]); + expect(Chord.tokenize("C64")).toEqual(["C", "64"]); // see: https://github.com/tonaljs/tonal/issues/70 - expect(tokenize("C5")).toEqual(["C", "5"]); - expect(tokenize("C4")).toEqual(["C", "4"]); + expect(Chord.tokenize("C5")).toEqual(["C", "5"]); + expect(Chord.tokenize("C4")).toEqual(["C", "4"]); }); test("chord", () => { - expect(chord("Cmaj7")).toEqual({ + expect(Chord.get("Cmaj7")).toEqual({ empty: false, name: "C major seventh", tonic: "C", @@ -42,13 +35,13 @@ describe("tonal-chord", () => { notes: ["C", "E", "G", "B"], quality: "Major" }); - expect(chord("hello").empty).toBe(true); - expect(chord("").empty).toBe(true); - expect(chord("C")).toEqual(chord("C major")); + expect(Chord.get("hello").empty).toBe(true); + expect(Chord.get("").empty).toBe(true); + expect(Chord.get("C")).toEqual(Chord.get("C major")); }); test("chord without tonic", () => { - expect(chord("dim")).toEqual({ + expect(Chord.get("dim")).toEqual({ aliases: ["dim", "°", "o"], chroma: "100100100000", empty: false, @@ -61,63 +54,69 @@ describe("tonal-chord", () => { tonic: "", type: "diminished" }); - expect(chord("dim7")).toMatchObject({ name: "diminished seventh" }); - expect(chord("alt7")).toMatchObject({ name: "altered" }); + expect(Chord.get("dim7")).toMatchObject({ name: "diminished seventh" }); + expect(Chord.get("alt7")).toMatchObject({ name: "altered" }); }); test("notes", () => { - expect(chord("Cmaj7").notes).toEqual(["C", "E", "G", "B"]); - expect(chord("Eb7add6").notes).toEqual(["Eb", "G", "Bb", "Db", "C"]); - expect(chord("C4 maj7").notes).toEqual(["C4", "E4", "G4", "B4"]); - expect(chord("C7").notes).toEqual(["C", "E", "G", "Bb"]); - expect(chord("Cmaj7#5").notes).toEqual(["C", "E", "G#", "B"]); - expect(chord("blah").notes).toEqual([]); + expect(Chord.get("Cmaj7").notes).toEqual(["C", "E", "G", "B"]); + expect(Chord.get("Eb7add6").notes).toEqual(["Eb", "G", "Bb", "Db", "C"]); + expect(Chord.get("C4 maj7").notes).toEqual(["C4", "E4", "G4", "B4"]); + expect(Chord.get("C7").notes).toEqual(["C", "E", "G", "Bb"]); + expect(Chord.get("Cmaj7#5").notes).toEqual(["C", "E", "G#", "B"]); + expect(Chord.get("blah").notes).toEqual([]); }); test("notes with two params", () => { - expect(chord(["C", "maj7"]).notes).toEqual(["C", "E", "G", "B"]); + expect(Chord.get(["C", "maj7"]).notes).toEqual(["C", "E", "G", "B"]); // see: https://github.com/danigb/tonal/issues/82 - expect(chord(["C6", "maj7"]).notes).toEqual(["C6", "E6", "G6", "B6"]); + expect(Chord.get(["C6", "maj7"]).notes).toEqual(["C6", "E6", "G6", "B6"]); }); // see: https://github.com/danigb/tonal/issues/52 test("augmented chords (issue #52)", () => { - expect(chord("Caug").notes).toEqual(["C", "E", "G#"]); - expect(chord(["C", "aug"]).notes).toEqual(["C", "E", "G#"]); + expect(Chord.get("Caug").notes).toEqual(["C", "E", "G#"]); + expect(Chord.get(["C", "aug"]).notes).toEqual(["C", "E", "G#"]); }); test("intervals", () => { - expect(chord("maj7").intervals).toEqual(["1P", "3M", "5P", "7M"]); - expect(chord("Cmaj7").intervals).toEqual(["1P", "3M", "5P", "7M"]); - expect(chord("aug").intervals).toEqual(["1P", "3M", "5A"]); - expect(chord("C13no5").intervals).toEqual(["1P", "3M", "7m", "9M", "13M"]); - expect(chord("major").intervals).toEqual(["1P", "3M", "5P"]); + expect(Chord.get("maj7").intervals).toEqual(["1P", "3M", "5P", "7M"]); + expect(Chord.get("Cmaj7").intervals).toEqual(["1P", "3M", "5P", "7M"]); + expect(Chord.get("aug").intervals).toEqual(["1P", "3M", "5A"]); + expect(Chord.get("C13no5").intervals).toEqual([ + "1P", + "3M", + "7m", + "9M", + "13M" + ]); + expect(Chord.get("major").intervals).toEqual(["1P", "3M", "5P"]); }); test("exists", () => { - expect(!chord("maj7").empty).toBe(true); - expect(!chord("Cmaj7").empty).toBe(true); - expect(!chord("mixolydian").empty).toBe(false); + expect(Chord.get("maj7").empty).toBe(false); + expect(Chord.get("Cmaj7").empty).toBe(false); + expect(Chord.get("mixolydian").empty).toBe(true); }); test("chordScales", () => { const names = "phrygian dominant,flamenco,spanish heptatonic,half-whole diminished,chromatic"; - expect(chordScales("C7b9")).toEqual(names.split(",")); + expect(Chord.chordScales("C7b9")).toEqual(names.split(",")); }); it("transpose chord names", () => { - expect(transpose("Eb7b9", "5P")).toEqual("Bb7b9"); + expect(Chord.transpose("Eb7b9", "5P")).toEqual("Bb7b9"); }); test("extended", () => { const chords = "Cmaj#4 Cmaj7#9#11 Cmaj9 CM7add13 Cmaj13 Cmaj9#11 CM13#11 CM7b9"; - expect(extended("CMaj7").sort()).toEqual($(chords).sort()); + expect(Chord.extended("CMaj7").sort()).toEqual($(chords).sort()); }); test("reduced", () => { - expect(reduced("CMaj7")).toEqual(["C5", "CM"]); + expect(Chord.reduced("CMaj7")).toEqual(["C5", "CM"]); }); /* diff --git a/packages/chord/index.ts b/packages/chord/index.ts index 9a6c09bf..0cf2035f 100644 --- a/packages/chord/index.ts +++ b/packages/chord/index.ts @@ -1,16 +1,22 @@ +import { detect } from "@tonaljs/chord-detect"; import { + all as chordTypes, ChordType, - entries as chordTypes, get as getChordType -} from "@tonaljs/chord-dictionary"; +} from "@tonaljs/chord-type"; + import { + deprecate, note, NoteName, tokenizeNote, transpose as transposeNote } from "@tonaljs/core"; + import { isSubsetOf, isSupersetOf, modes } from "@tonaljs/pcset"; -import { entries as scaleTypes } from "@tonaljs/scale-dictionary"; + +import { all as scaleTypes } from "@tonaljs/scale-type"; +export { detect } from "@tonaljs/chord-detect"; type ChordName = string; type ChordNameTokens = [string, string]; // [TONIC, SCALE TYPE] @@ -78,7 +84,7 @@ export function tokenize(name: string): ChordNameTokens { /** * Get a Chord from a chord name. */ -export function chord(src: ChordName | ChordNameTokens): Chord { +export function get(src: ChordName | ChordNameTokens): Chord { const { type, tonic } = findChord(src); if (!type || type.empty) { @@ -92,6 +98,8 @@ export function chord(src: ChordName | ChordNameTokens): Chord { return { ...type, name, type: type.name, tonic: tonic || "", notes }; } +export const chord = deprecate("Chord.chord", "Chord.get", get); + function findChord(src: string | ChordNameTokens) { if (!src || !src.length) { return {}; @@ -134,7 +142,7 @@ export function transpose(chordName: string, interval: string): string { * // => ["phrygian dominant", "flamenco", "spanish heptatonic", "half-whole diminished", "chromatic"] */ export function chordScales(name: string): string[] { - const s = chord(name); + const s = get(name); const isChordIncluded = isSupersetOf(s.chroma); return scaleTypes() .filter(scale => isChordIncluded(scale.chroma)) @@ -150,7 +158,7 @@ export function chordScales(name: string): string[] { * // => [ 'Cmaj#4', 'Cmaj7#9#11', 'Cmaj9', 'CM7add13', 'Cmaj13', 'Cmaj9#11', 'CM13#11', 'CM7b9' ] */ export function extended(chordName: string): string[] { - const s = chord(chordName); + const s = get(chordName); const isSuperset = isSupersetOf(s.chroma); return chordTypes() .filter(chord => isSuperset(chord.chroma)) @@ -164,9 +172,21 @@ export function extended(chordName: string): string[] { * @example */ export function reduced(chordName: string): string[] { - const s = chord(chordName); + const s = get(chordName); const isSubset = isSubsetOf(s.chroma); return chordTypes() .filter(chord => isSubset(chord.chroma)) .map(chord => s.tonic + chord.aliases[0]); } + +export default { + get, + detect, + chordScales, + extended, + reduced, + tokenize, + transpose, + // deprecate + chord +}; diff --git a/packages/chord/package.json b/packages/chord/package.json index e3bbe3fd..dcda04bb 100644 --- a/packages/chord/package.json +++ b/packages/chord/package.json @@ -16,11 +16,12 @@ ], "types": "dist/chord/index.d.ts", "dependencies": { - "@tonaljs/array": "^3.2.4", - "@tonaljs/chord-dictionary": "^3.4.1", + "@tonaljs/collection": "^3.2.4", + "@tonaljs/chord-type": "^3.4.1", + "@tonaljs/chord-detect": "^3.4.5", "@tonaljs/core": "^3.3.0", "@tonaljs/pcset": "^3.2.4", - "@tonaljs/scale-dictionary": "^3.4.3" + "@tonaljs/scale-type": "^3.4.3" }, "author": "danigb@gmail.com", "license": "MIT", diff --git a/packages/collection/LICENSE b/packages/collection/LICENSE new file mode 100644 index 00000000..77ac35ab --- /dev/null +++ b/packages/collection/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 danigb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/packages/collection/README.md b/packages/collection/README.md new file mode 100644 index 00000000..f265a2fd --- /dev/null +++ b/packages/collection/README.md @@ -0,0 +1,61 @@ +# @tonaljs/collection ![tonal](https://img.shields.io/badge/@tonaljs-collection-yellow.svg?style=flat-square) [![npm version](https://img.shields.io/npm/v/@tonaljs/collection.svg?style=flat-square)](https://www.npmjs.com/package/@tonaljs/collection) + +> Functions to create and manipulate collections (collections) + +## Usage + +ES6: + +```js +import { Collection } from "@tonaljs/tonal"; +``` + +node: + +```js +const { Collection } = require("@tonaljs/tonal"); +``` + +## API + +#### `range(from: number, to: number) => number[]` + +Creates a numeric range: + +```js +Collection.range(-2, 2); // => [-2, -1, 0, 1, 2] +Collection.range(2, -2); // => [2, 1, 0, -1, -2] +``` + +#### `rotate(times: number, collection: any[]) => any[]` + +Rotate an collection a number of times: + +```js +Collection.rotate(1, [1, 2, 3]); // => [2, 3, 1] +``` + +#### `shuffle(collection: any[]) => any[]` + +Randomizes the order of the specified collection in-place, using the Fisher–Yates shuffle. + +```js +Collection.shuffle(["a", "b", "c"]); +``` + +#### `permutations(collection: any[]) => any[][]` + +Get all permutations of an collection + +```js +Collection.permutations(["a", "b", "c"])) // => +// => +// [ +// ["a", "b", "c"], +// ["b", "a", "c"], +// ["b", "c", "a"], +// ["a", "c", "b"], +// ["c", "a", "b"], +// ["c", "b", "a"] +// ] +``` diff --git a/packages/collection/collection.test.ts b/packages/collection/collection.test.ts new file mode 100644 index 00000000..57e4c28c --- /dev/null +++ b/packages/collection/collection.test.ts @@ -0,0 +1,36 @@ +import Collection from "./index"; + +const $ = (arr: string) => arr.split(" "); + +describe("@tonaljs/collection", () => { + test("range", () => { + expect(Collection.range(-2, 2)).toEqual([-2, -1, 0, 1, 2]); + expect(Collection.range(2, -2)).toEqual([2, 1, 0, -1, -2]); + }); + + test("rotate", () => { + expect(Collection.rotate(2, $("a b c d e"))).toEqual($("c d e a b")); + }); + + test("compact", () => { + const input = ["a", 1, 0, true, false, null, undefined]; + const result = ["a", 1, 0, true]; + expect(Collection.compact(input)).toEqual(result); + }); + + test("shuffle", () => { + const rnd = () => 0.2; + expect(Collection.shuffle($("a b c d"), rnd)).toEqual(["b", "c", "d", "a"]); + }); + + it("permutations", () => { + expect(Collection.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/collection/index.ts b/packages/collection/index.ts new file mode 100644 index 00000000..41f0b669 --- /dev/null +++ b/packages/collection/index.ts @@ -0,0 +1,121 @@ +// ascending range +function ascR(b: number, n: number) { + const a = []; + // tslint:disable-next-line:curly + for (; n--; a[n] = n + b); + return a; +} +// descending range +function descR(b: number, n: number) { + const a = []; + // tslint:disable-next-line:curly + for (; n--; a[n] = b - n); + return a; +} + +/** + * Creates a numeric range + * + * @param {number} from + * @param {number} to + * @return {Array} + * + * @example + * range(-2, 2) // => [-2, -1, 0, 1, 2] + * range(2, -2) // => [2, 1, 0, -1, -2] + */ +export function range(from: number, to: number): number[] { + return from < to ? ascR(from, to - from + 1) : descR(from, from - to + 1); +} + +/** + * Rotates a list a number of times. It"s completly agnostic about the + * contents of the list. + * + * @param {Integer} times - the number of rotations + * @param {Array} collection + * @return {Array} the rotated collection + * + * @example + * rotate(1, [1, 2, 3]) // => [2, 3, 1] + */ +export function rotate(times: number, arr: T[]): T[] { + const len = arr.length; + const n = ((times % len) + len) % len; + return arr.slice(n, len).concat(arr.slice(0, n)); +} + +/** + * Return a copy of the collection with the null values removed + * @function + * @param {Array} collection + * @return {Array} + * + * @example + * compact(["a", "b", null, "c"]) // => ["a", "b", "c"] + */ +export function compact(arr: any[]): any[] { + return arr.filter(n => n === 0 || n); +} + +/** + * Randomizes the order of the specified collection in-place, using the Fisher–Yates shuffle. + * + * @function + * @param {Array} collection + * @return {Array} the collection shuffled + * + * @example + * shuffle(["C", "D", "E", "F"]) // => [...] + */ +export function shuffle(arr: any[], rnd = Math.random): any[] { + let i: number; + let t: any; + let m: number = arr.length; + while (m) { + i = Math.floor(rnd() * m--); + t = arr[m]; + arr[m] = arr[i]; + arr[i] = t; + } + return arr; +} + +/** + * Get all permutations of an collection + * + * @param {Array} collection - the collection + * @return {Array} an collection with all the permutations + * @example + * permutations(["a", "b", "c"])) // => + * [ + * ["a", "b", "c"], + * ["b", "a", "c"], + * ["b", "c", "a"], + * ["a", "c", "b"], + * ["c", "a", "b"], + * ["c", "b", "a"] + * ] + */ +export function permutations(arr: any[]): any[] { + if (arr.length === 0) { + return [[]]; + } + return permutations(arr.slice(1)).reduce((acc, perm) => { + return acc.concat( + arr.map((e, pos) => { + const newPerm = perm.slice(); + newPerm.splice(pos, 0, arr[0]); + return newPerm; + }) + ); + }, []); +} + +export default { + compact, + permutations, + range, + rotate, + shuffle +}; diff --git a/packages/collection/package.json b/packages/collection/package.json new file mode 100644 index 00000000..14b4e728 --- /dev/null +++ b/packages/collection/package.json @@ -0,0 +1,22 @@ +{ + "name": "@tonaljs/collection", + "version": "3.2.4", + "description": "Utility functions to work with collections (arrays)", + "keywords": [ + "collection", + "music", + "theory" + ], + "main": "dist/index.es5.js", + "module": "dist/index.esnext.js", + "files": [ + "dist" + ], + "types": "dist/collection/index.d.ts", + "dependencies": {}, + "author": "danigb@gmail.com", + "license": "MIT", + "publishConfig": { + "access": "public" + } +} diff --git a/packages/core/README.md b/packages/core/README.md index 68785b04..08247cea 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -4,9 +4,11 @@ `@tonaljs/core` is the core module of the `tonal` music theory library. -It exports just four functions. +Normally you don't use this module directly. -Two of them converts strings into data structures (`note` and `interval`): +## Example + +Get note and interval properties: ```js import { note, interval } from "@tonaljs/core"; @@ -14,7 +16,7 @@ note("c4"); // => { name: 'C4', oct: 4, ...} interval("p5"); // => { name: '5P', semitones: 7, ...} ``` -The other two manipulates notes and intervals, by it's string name representation: +Transpose notes and calculate intervals: ```js import { transpose, distance } from "@tonaljs/core"; @@ -22,8 +24,6 @@ transpose("C4", "5P"); // => "G4" distance("C4", "G4"); // => "5P" ``` -This is a common pattern in tonal packages: functions to parse strings, functions to manipulate strings. - ## API ### `note(name: string) => Note` @@ -39,7 +39,7 @@ Given a note name, it returns an object with the following properties: - oct: the octave (or null if not present) - chroma: the note chroma (0..11) - midi: the note midi or null if octave is not present -- freq: the note frecuency in Hertzs, or null if the octave is note present +- freq: the note frequency in Hertzes, or null if the octave is note present Example: diff --git a/packages/core/index.ts b/packages/core/index.ts index 316f5c33..6de81a4f 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -1,35 +1,6 @@ -export { Named, NotFound, isNamed } from "./src/tonal"; - -export { - Pitch, - NoteCoordinates, - IntervalCoordinates, - isPitch, - encode, - decode -} from "./src/pitch"; - -export { - note, - tokenize as tokenizeNote, - NoteName, - Note, - NoteLiteral, - NoNote, - PcName, - altToAcc, - accToAlt, - coordToNote -} from "./src/note"; - -export { - interval, - tokenize as tokenizeInterval, - Interval, - IntervalLiteral, - IntervalName, - NoInterval, - coordToInterval -} from "./src/interval"; - -export { transpose, distance } from "./src/distance"; +export * from "./src/utils"; +export * from "./src/named"; +export * from "./src/pitch"; +export * from "./src/note"; +export * from "./src/interval"; +export * from "./src/distance"; diff --git a/packages/core/src/interval.ts b/packages/core/src/interval.ts index 48a58cf2..35dfceaa 100644 --- a/packages/core/src/interval.ts +++ b/packages/core/src/interval.ts @@ -1,3 +1,4 @@ +import { isNamed, Named } from "./named"; import { decode, Direction, @@ -7,7 +8,7 @@ import { Pitch, PitchCoordinates } from "./pitch"; -import { isNamed, Named } from "./tonal"; +import { fillStr } from "./utils"; export type IntervalName = string; export type IntervalLiteral = IntervalName | Pitch | Named; @@ -63,7 +64,7 @@ type IntervalTokens = [string, string]; /** * @private */ -export function tokenize(str?: IntervalName): IntervalTokens { +export function tokenizeInterval(str?: IntervalName): IntervalTokens { const m = REGEX.exec(`${str}`); if (m === null) { return ["", ""]; @@ -106,7 +107,7 @@ export function interval(src: IntervalLiteral): Interval | NoInterval { const SIZES = [0, 2, 4, 5, 7, 9, 11]; const TYPES = "PMMPPMM"; function parse(str?: string): Interval | NoInterval { - const tokens = tokenize(str); + const tokens = tokenizeInterval(str); if (tokens[0] === "") { return NoInterval; } @@ -180,8 +181,6 @@ function pitchName(props: Pitch): string { return name; } -const fillStr = (s: string, n: number) => Array(Math.abs(n) + 1).join(s); - function altToQ(type: Type, alt: number): Quality { if (alt === 0) { return type === "majorable" ? "M" : "P"; diff --git a/packages/core/src/tonal.ts b/packages/core/src/named.ts similarity index 54% rename from packages/core/src/tonal.ts rename to packages/core/src/named.ts index e0b80f1d..55b255e9 100644 --- a/packages/core/src/tonal.ts +++ b/packages/core/src/named.ts @@ -2,11 +2,17 @@ export interface Named { readonly name: string; } +export interface NamedFound { + readonly empty: false; +} + export interface NotFound extends Named { readonly empty: true; readonly name: ""; } export function isNamed(src: any): src is Named { - return typeof src === "object" && typeof src.name === "string"; + return src !== null && typeof src === "object" && typeof src.name === "string" + ? true + : false; } diff --git a/packages/core/src/note.ts b/packages/core/src/note.ts index c73c8189..c097e8d0 100644 --- a/packages/core/src/note.ts +++ b/packages/core/src/note.ts @@ -1,5 +1,6 @@ +import { isNamed, Named } from "./named"; import { decode, encode, isPitch, Pitch, PitchCoordinates } from "./pitch"; -import { isNamed, Named } from "./tonal"; +import { fillStr } from "./utils"; export type NoteWithOctave = string; export type PcName = string; @@ -27,9 +28,8 @@ export interface NoNote extends Partial { } const NoNote: NoNote = { empty: true, name: "", pc: "", acc: "" }; -const cache: Record = {}; +const cache: Map = new Map(); -const fillStr = (s: string, n: number) => Array(n + 1).join(s); export const stepToLetter = (step: number) => "CDEFGAB".charAt(step); export const altToAcc = (alt: number): string => alt < 0 ? fillStr("b", -alt) : fillStr("#", alt); @@ -42,22 +42,31 @@ export const accToAlt = (acc: string): number => * note('Bb4') // => { name: "Bb4", midi: 70, chroma: 10, ... } */ export function note(src: NoteLiteral): Note | NoNote { - return typeof src === "string" - ? cache[src] || (cache[src] = parse(src)) - : isPitch(src) - ? note(pitchName(src)) - : isNamed(src) - ? note(src.name) - : NoNote; + const cached = cache.get(src); + if (cached) { + return cached; + } + + const value = + typeof src === "string" + ? parse(src) + : isPitch(src) + ? note(pitchName(src)) + : isNamed(src) + ? note(src.name) + : NoNote; + cache.set(src, value); + return value; } type NoteTokens = [string, string, string, string]; const REGEX = /^([a-gA-G]?)(#{1,}|b{1,}|x{1,}|)(-?\d*)\s*(.*)$/; + /** * @private */ -export function tokenize(str: string): NoteTokens { +export function tokenizeNote(str: string): NoteTokens { const m = REGEX.exec(str) as string[]; return [m[1].toUpperCase(), m[2].replace(/x/g, "##"), m[3], m[4]]; } @@ -71,7 +80,7 @@ export function coordToNote(noteCoord: PitchCoordinates): Note { const SEMI = [0, 2, 4, 5, 7, 9, 11]; function parse(noteName: NoteName): Note | NoNote { - const tokens = tokenize(noteName); + const tokens = tokenizeNote(noteName); if (tokens[0] === "" || tokens[3] !== "") { return NoNote; } diff --git a/packages/core/src/pitch.ts b/packages/core/src/pitch.ts index 39487401..d77a7da2 100644 --- a/packages/core/src/pitch.ts +++ b/packages/core/src/pitch.ts @@ -1,4 +1,4 @@ -import { Named } from "./tonal"; +import { Named } from "./named"; type Fifths = number; type Octaves = number; @@ -28,14 +28,15 @@ export interface Pitch { } export function isPitch(pitch: any): pitch is Pitch { - return ( + return pitch !== null && typeof pitch === "object" && typeof pitch.step === "number" && typeof pitch.alt === "number" - ); + ? true + : false; } -// The nuuber of fifths of [C, D, E, F, G, A, B] +// The number of fifths of [C, D, E, F, G, A, B] const FIFTHS = [0, 2, 4, -1, 1, 3, 5]; // The number of octaves it span each step const STEPS_TO_OCTS = FIFTHS.map((fifths: number) => diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts new file mode 100644 index 00000000..6814b5b3 --- /dev/null +++ b/packages/core/src/utils.ts @@ -0,0 +1,17 @@ +/** + * Fill a string with a repeated character + * + * @param character + * @param repetition + */ +export const fillStr = (s: string, n: number) => Array(Math.abs(n) + 1).join(s); + +export function deprecate< + ResultFn extends (this: any, ...newArgs: any[]) => ReturnType +>(original: string, alternative: string, fn: ResultFn) { + return function(this: unknown, ...args: unknown[]): ReturnType { + // tslint:disable-next-line + console.warn(`${original} is deprecated. Use ${alternative}.`); + return fn.apply(this, args); + }; +} diff --git a/packages/core/test/test.tonal.test.ts b/packages/core/test/tonal.named.test.ts similarity index 89% rename from packages/core/test/test.tonal.test.ts rename to packages/core/test/tonal.named.test.ts index a0e7cf99..27f15060 100644 --- a/packages/core/test/test.tonal.test.ts +++ b/packages/core/test/tonal.named.test.ts @@ -7,6 +7,7 @@ describe("@tonaljs/core", () => { expect(isNamed(note("X"))).toBe(true); expect(isNamed(undefined)).toBe(false); + expect(isNamed(null)).toBe(false); expect(isNamed("")).toBe(false); }); }); diff --git a/packages/core/test/test.pitch.test.ts b/packages/core/test/tonal.pitch.test.ts similarity index 100% rename from packages/core/test/test.pitch.test.ts rename to packages/core/test/tonal.pitch.test.ts diff --git a/packages/core/test/tonal.utils.test.ts b/packages/core/test/tonal.utils.test.ts new file mode 100644 index 00000000..893410e1 --- /dev/null +++ b/packages/core/test/tonal.utils.test.ts @@ -0,0 +1,7 @@ +import { fillStr } from "../index"; + +describe("@tonaljs/core", () => { + test("fillStr", () => { + expect(fillStr("#", 5)).toEqual("#####"); + }); +}); diff --git a/packages/interval/README.md b/packages/interval/README.md index 02d2659d..07be22c3 100644 --- a/packages/interval/README.md +++ b/packages/interval/README.md @@ -2,55 +2,106 @@ > A collection of functions to create and manipulate music intervals +## Usage + +ES6: + +```js +import { Interval } from "@tonaljs/tonal"; +``` + +nodejs: + +```js +const { Interval } = require("@tonaljs/tonal"); +``` + ## API -### `names() => string[]` +#### `Interval.get(name: string)` + +Get properties of an interval: + +- name: the interval name (number + quality) +- type: "perfectable" | "majorable" +- dir: direction: 1 | -1 +- num: the interval number +- q: quality (...| 'dd' | 'd' | 'm' | 'M' | 'A' | ...) +- alt: the quality number as a number +- oct: the number of octaves it spans +- semitones: the number of semitones it spans +- simple: the simplified number + +```js +Interval.get("5P"); // => { name: "5P", num: 5, ...} +``` + +There are some shorthand functions (`name`, `num`, `semitones`, `quality`): + +```js +Interval.name("d4"); // => "4d" +Interval.num("5P"); // => 5 +Interval.quality("5P"); // => "P" +Interval.semitones("P4"); // => 5 +``` + +#### `distance(from: string, to: string) => string` + +Find the interval between two notes. + +```js +Interval.distance("C4", "G4"); // => "5P" +``` + +#### `names() => string[]` Return a list of (natural) interval names: ```js -names(); // => ["1P", "2M", "3M", "4P", "5P", "6m", "7m"] +Interval.names(); // => ["1P", "2M", "3M", "4P", "5P", "6m", "7m"] ``` -### `fromSemitones(semitones: number) => string` +#### `fromSemitones(semitones: number) => string` Given a number of semitones, returns the interval name: ```js -fromSemitones(7); // => "5P" -fromSemitones(-7); // => "-5P" +Interval.fromSemitones(7); // => "5P" +Interval.fromSemitones(-7); // => "-5P" + +[0, 1, 2, 3, 4].map(Interval.fromSemitones); ``` -### `simplify(interval: string) => string` +#### `simplify(interval: string) => string` Simplify an interval: ```js -simplify("9M"); // => "2M" -["8P", "9M", "10M", "11P", "12P", "13M", "14M", "15P"].map(simplify); +Interval.simplify("9M"); // => "2M" +Interval.simplify("2M"); // => "2M" +Interval.simplify("-2M"); // => "7m" +["8P", "9M", "10M", "11P", "12P", "13M", "14M", "15P"].map(Interval.simplify); // => [ "8P", "2M", "3M", "4P", "5P", "6M", "7M", "8P" ] -simplify("2M"); // => "2M" -simplify("-2M"); // => "7m" ``` -### `invert(interval: string) => string` +#### `invert(interval: string) => string` Get the interval inversion: ```js -invert("3m"); // => "6M" -invert("2M"); // => "7m" +Interval.invert("3m"); // => "6M" +Interval.invert("2M"); // => "7m" ``` -### `add(a: string, b: string) => string` +#### `add(a: string, b: string) => string` Add two intervals: ```js -add("3m", "5P"); // => "7m" +Interval.add("3m", "5P"); // => "7m" ``` -### `substract(min: string, sub: string) => string` +#### `substract(min: string, sub: string) => string` Substract two intervals: diff --git a/packages/interval/index.ts b/packages/interval/index.ts index ca773077..026fcf31 100644 --- a/packages/interval/index.ts +++ b/packages/interval/index.ts @@ -1,10 +1,11 @@ -export { tokenizeInterval as tokenize } from "@tonaljs/core"; import { coordToInterval, - interval, + distance as dist, + interval as props, IntervalCoordinates, IntervalName, - NoteCoordinates + NoteCoordinates, + tokenizeInterval as tokenize } from "@tonaljs/core"; /** @@ -14,6 +15,50 @@ export function names(): IntervalName[] { return "1P 2M 3M 4P 5P 6m 7m".split(" "); } +/** + * Get properties of an interval + * + * @function + * @example + * Interval.get('P4') // => {"alt": 0, "dir": 1, "name": "4P", "num": 4, "oct": 0, "q": "P", "semitones": 5, "simple": 4, "step": 3, "type": "perfectable"} + */ +export const get = props; + +/** + * Get name of an interval + * + * @function + * @example + * Interval.name('4P') // => "4P" + * Interval.name('P4') // => "4P" + * Interval.name('C4') // => "" + */ +export const name = (name: string) => props(name).name; + +/** + * Get semitones of an interval + * @function + * @example + * Interval.semitones('P4') // => 5 + */ +export const semitones = (name: string) => props(name).semitones; + +/** + * Get quality of an interval + * @function + * @example + * Interval.quality('P4') // => "P" + */ +export const quality = (name: string) => props(name).q; + +/** + * Get number of an interval + * @function + * @example + * Interval.num('P4') // => 4 + */ +export const num = (name: string) => props(name).num; + /** * Get the simplified version of an interval. * @@ -22,14 +67,14 @@ export function names(): IntervalName[] { * @return {string} the simplified interval * * @example - * simplify("9M") // => "2M" - * ["8P", "9M", "10M", "11P", "12P", "13M", "14M", "15P"].map(simplify) + * Interval.simplify("9M") // => "2M" + * Interval.simplify("2M") // => "2M" + * Interval.simplify("-2M") // => "7m" + * ["8P", "9M", "10M", "11P", "12P", "13M", "14M", "15P"].map(Interval.simplify) * // => [ "8P", "2M", "3M", "4P", "5P", "6M", "7M", "8P" ] - * simplify("2M") // => "2M" - * simplify("-2M") // => "7m" */ export function simplify(name: IntervalName): IntervalName { - const i = interval(name); + const i = props(name); return i.empty ? "" : i.simple + i.q; } @@ -43,17 +88,17 @@ export function simplify(name: IntervalName): IntervalName { * @return {string} the inverted interval * * @example - * invert("3m") // => "6M" - * invert("2M") // => "7m" + * Interval.invert("3m") // => "6M" + * Interval.invert("2M") // => "7m" */ export function invert(name: IntervalName): IntervalName { - const i = interval(name); + const i = props(name); if (i.empty) { return ""; } const step = (7 - i.step) % 7; const alt = i.type === "perfectable" ? -i.alt : -(i.alt + 1); - return interval({ step, alt, oct: i.oct, dir: i.dir }).name; + return props({ step, alt, oct: i.oct, dir: i.dir }).name; } // interval numbers @@ -63,13 +108,13 @@ const IQ = "P m M m M P d P m M m M".split(" "); /** * Get interval name from semitones number. Since there are several interval - * names for the same number, the name it"s arbitraty, but deterministic. + * names for the same number, the name it's arbitrary, but deterministic. * * @param {Integer} num - the number of semitones (can be negative) * @return {string} the interval name * @example - * fromSemitones(7) // => "5P" - * fromSemitones(-7) // => "-5P" + * Interval.fromSemitones(7) // => "5P" + * Interval.fromSemitones(-7) // => "-5P" */ export function fromSemitones(semitones: number): IntervalName { const d = semitones < 0 ? -1 : 1; @@ -79,21 +124,13 @@ export function fromSemitones(semitones: number): IntervalName { return d * (IN[c] + 7 * o) + IQ[c]; } -type Operation = ( - a: IntervalCoordinates, - b: IntervalCoordinates -) => NoteCoordinates; - -function combine(fn: Operation) { - return (a: IntervalName, b: IntervalName): IntervalName | undefined => { - const coordA = interval(a).coord; - const coordB = interval(b).coord; - if (coordA && coordB) { - const coord = fn(coordA, coordB); - return coordToInterval(coord).name; - } - }; -} +/** + * Find interval between two notes + * + * @example + * Interval.distance("C4", "G4"); // => "5P" + */ +export const distance = dist; /** * Adds two intervals @@ -103,10 +140,19 @@ function combine(fn: Operation) { * @param {string} interval2 * @return {string} the added interval name * @example - * import { add } from "@tonaljs/core" - * add("3m", "5P") // => "7m" + * Interval.add("3m", "5P") // => "7m" + */ +export const add = combinator((a, b) => [a[0] + b[0], a[1] + b[1]]); + +/** + * Returns a function that adds an interval + * + * @function + * @example + * ['1P', '2M', '3M'].map(Interval.addTo('5P')) // => ["5P", "6M", "7M"] */ -export const add = combine((a, b) => [a[0] + b[0], a[1] + b[1]]); +export const addTo = (interval: string) => (other: string) => + add(interval, other); /** * Subtracts two intervals @@ -116,8 +162,41 @@ export const add = combine((a, b) => [a[0] + b[0], a[1] + b[1]]); * @param {string} subtrahendInterval * @return {string} the substracted interval name * @example - * import { substract } from '@tonaljs/core' - * substract('5P', '3M') // => '3m' - * substract('3M', '5P') // => '-3m' + * Interval.substract('5P', '3M') // => '3m' + * Interval.substract('3M', '5P') // => '-3m' */ -export const substract = combine((a, b) => [a[0] - b[0], a[1] - b[1]]); +export const substract = combinator((a, b) => [a[0] - b[0], a[1] - b[1]]); + +export default { + names, + get, + name, + num, + semitones, + quality, + fromSemitones, + distance, + invert, + simplify, + add, + addTo, + substract +}; + +//// PRIVATE //// + +type Operation = ( + a: IntervalCoordinates, + b: IntervalCoordinates +) => NoteCoordinates; + +function combinator(fn: Operation) { + return (a: IntervalName, b: IntervalName): IntervalName | undefined => { + const coordA = props(a).coord; + const coordB = props(b).coord; + if (coordA && coordB) { + const coord = fn(coordA, coordB); + return coordToInterval(coord).name; + } + }; +} diff --git a/packages/interval/interval.test.ts b/packages/interval/interval.test.ts index 287d3a90..6215a9d5 100644 --- a/packages/interval/interval.test.ts +++ b/packages/interval/interval.test.ts @@ -1,82 +1,117 @@ -import { - add, - fromSemitones, - invert, - names, - simplify, - substract -} from "./index"; +import Interval from "./index"; const $ = (str: string) => str.split(" "); describe("@tonaljs/interval", () => { + test("properties", () => { + expect(Interval.get("P4")).toEqual({ + alt: 0, + chroma: 5, + coord: [-1, 1], + dir: 1, + empty: false, + name: "4P", + num: 4, + oct: 0, + q: "P", + semitones: 5, + simple: 4, + step: 3, + type: "perfectable" + }); + }); + + test("shorthand properties", () => { + expect(Interval.name("d5")).toEqual("5d"); + expect(Interval.num("d5")).toEqual(5); + expect(Interval.quality("d5")).toEqual("d"); + expect(Interval.semitones("d5")).toEqual(6); + }); + + test("distance", () => { + expect(Interval.distance("C4", "G4")).toEqual("5P"); + }); + test("names", () => { - expect(names()).toEqual(["1P", "2M", "3M", "4P", "5P", "6m", "7m"]); + expect(Interval.names()).toEqual([ + "1P", + "2M", + "3M", + "4P", + "5P", + "6m", + "7m" + ]); }); test("simplify intervals", () => { - expect($("1P 2M 3M 4P 5P 6M 7M").map(simplify)).toEqual( + expect($("1P 2M 3M 4P 5P 6M 7M").map(Interval.simplify)).toEqual( $("1P 2M 3M 4P 5P 6M 7M") ); - expect($("8P 9M 10M 11P 12P 13M 14M").map(simplify)).toEqual( + expect($("8P 9M 10M 11P 12P 13M 14M").map(Interval.simplify)).toEqual( $("8P 2M 3M 4P 5P 6M 7M") ); - expect($("1d 1P 1A 8d 8P 8A 15d 15P 15A").map(simplify)).toEqual( + expect($("1d 1P 1A 8d 8P 8A 15d 15P 15A").map(Interval.simplify)).toEqual( $("1d 1P 1A 8d 8P 8A 1d 1P 1A") ); - expect($("-1P -2M -3M -4P -5P -6M -7M").map(simplify)).toEqual( + expect($("-1P -2M -3M -4P -5P -6M -7M").map(Interval.simplify)).toEqual( $("-1P -2M -3M -4P -5P -6M -7M") ); - expect($("-8P -9M -10M -11P -12P -13M -14M").map(simplify)).toEqual( - $("-8P -2M -3M -4P -5P -6M -7M") - ); + expect( + $("-8P -9M -10M -11P -12P -13M -14M").map(Interval.simplify) + ).toEqual($("-8P -2M -3M -4P -5P -6M -7M")); }); test("invert intervals", () => { - expect($("1P 2M 3M 4P 5P 6M 7M").map(invert)).toEqual( + expect($("1P 2M 3M 4P 5P 6M 7M").map(Interval.invert)).toEqual( $("1P 7m 6m 5P 4P 3m 2m") ); - expect($("1d 2m 3m 4d 5d 6m 7m").map(invert)).toEqual( + expect($("1d 2m 3m 4d 5d 6m 7m").map(Interval.invert)).toEqual( $("1A 7M 6M 5A 4A 3M 2M") ); - expect($("1A 2A 3A 4A 5A 6A 7A").map(invert)).toEqual( + expect($("1A 2A 3A 4A 5A 6A 7A").map(Interval.invert)).toEqual( $("1d 7d 6d 5d 4d 3d 2d") ); - expect($("-1P -2M -3M -4P -5P -6M -7M").map(invert)).toEqual( + expect($("-1P -2M -3M -4P -5P -6M -7M").map(Interval.invert)).toEqual( $("-1P -7m -6m -5P -4P -3m -2m") ); - expect($("8P 9M 10M 11P 12P 13M 14M").map(invert)).toEqual( + expect($("8P 9M 10M 11P 12P 13M 14M").map(Interval.invert)).toEqual( $("8P 14m 13m 12P 11P 10m 9m") ); }); test("fromSemitones", () => { let semis = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; - expect(semis.map(fromSemitones)).toEqual( + expect(semis.map(Interval.fromSemitones)).toEqual( $("1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M") ); semis = [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]; - expect(semis.map(fromSemitones)).toEqual( + expect(semis.map(Interval.fromSemitones)).toEqual( $("8P 9m 9M 10m 10M 11P 12d 12P 13m 13M 14m 14M") ); semis = [-0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11]; - expect(semis.map(fromSemitones)).toEqual( + expect(semis.map(Interval.fromSemitones)).toEqual( $("1P -2m -2M -3m -3M -4P -5d -5P -6m -6M -7m -7M") ); semis = [-12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23]; - expect(semis.map(fromSemitones)).toEqual( + expect(semis.map(Interval.fromSemitones)).toEqual( $("-8P -9m -9M -10m -10M -11P -12d -12P -13m -13M -14m -14M") ); }); test("add", () => { - expect(add("3m", "5P")).toEqual("7m"); - expect(names().map(n => add("5P", n))).toEqual($("5P 6M 7M 8P 9M 10m 11P")); + expect(Interval.add("3m", "5P")).toEqual("7m"); + expect(Interval.names().map(n => Interval.add("5P", n))).toEqual( + $("5P 6M 7M 8P 9M 10m 11P") + ); + expect(Interval.names().map(Interval.addTo("5P"))).toEqual( + $("5P 6M 7M 8P 9M 10m 11P") + ); }); test("substract", () => { - expect(substract("5P", "3M")).toEqual("3m"); - expect(substract("3M", "5P")).toEqual("-3m"); - expect(names().map(n => substract("5P", n))).toEqual( + expect(Interval.substract("5P", "3M")).toEqual("3m"); + expect(Interval.substract("3M", "5P")).toEqual("-3m"); + expect(Interval.names().map(n => Interval.substract("5P", n))).toEqual( $("5P 4P 3m 2M 1P -2m -3m") ); }); diff --git a/packages/key/README.md b/packages/key/README.md index aadd40d4..86c181d0 100644 --- a/packages/key/README.md +++ b/packages/key/README.md @@ -4,42 +4,28 @@ Get scale and chords of major and minor keys. -## Install - -```bash -npm i --save @tonaljs/key -# or -yarn add @tonaljs/key -``` - ## Usage -With ES6 `import`: +ES6: ```js -import { majorKey } from '@tonaljs/key'; -// or -import * as Key from '@tonaljs/key'; +import { Key } from "@tonaljs/tonal"; ``` -With ES5 `require`: +nodejs: ```js -const { majorKey } = require('@tonaljs/key'); -// or -const Key = require('@tonaljs/key'); +const { Key } = require("@tonaljs/tonal"); ``` - ## API - -### `majorKey(tonic: string) => MajorKey` +#### `majorKey(tonic: string) => MajorKey` Major key properties for a given tonic. Example: ```js -majorKey('C') // => +Key.majorKey('C') // => { tonic: "C", type: "major", @@ -58,12 +44,12 @@ majorKey('C') // => } ``` -### `minorKey(tonic: string) => MinorKey` +#### `minorKey(tonic: string) => MinorKey` Minor key properties for a given tonic. Example: ```js -minorKey('C') // => +Key.minorKey('C') // => { tonic: "C", type: "minor", @@ -102,19 +88,18 @@ minorKey('C') // => } ``` -### `majorTonicFromKeySignature(keySignature: string)` +#### `majorTonicFromKeySignature(keySignature: string)` Example: ```js -majorTonicFromKeySignature('bbb') // => Eb +Key.majorTonicFromKeySignature("bbb"); // => Eb ``` - ## HOW TO -#### How to get minor tonic from key signature +##### How to get minor tonic from key signature ```js -majorKey(majorTonicFromKeySignature('###')).minorRelative // => 'F#' +majorKey(majorTonicFromKeySignature("###")).relativeMinor; // => 'F#' ``` diff --git a/packages/key/index.ts b/packages/key/index.ts index 1f146ca8..00825953 100644 --- a/packages/key/index.ts +++ b/packages/key/index.ts @@ -1,6 +1,6 @@ import { accToAlt, altToAcc, note, transpose } from "@tonaljs/core"; import { transposeFifths } from "@tonaljs/note"; -import { romanNumeral } from "@tonaljs/roman-numeral"; +import { get as roman } from "@tonaljs/roman-numeral"; export interface Key { type: "major" | "minor"; @@ -49,7 +49,7 @@ function keyScale( ) { return (tonic: string): KeyScale => { const grades = gradesLiteral.split(" "); - const intervals = grades.map(gr => romanNumeral(gr).interval || ""); + const intervals = grades.map(gr => roman(gr).interval || ""); const scale = intervals.map(interval => transpose(tonic, interval)); const map = mapToScale(scale); @@ -156,3 +156,5 @@ export function majorTonicFromKeySignature( } return null; } + +export default { majorKey, majorTonicFromKeySignature, minorKey }; diff --git a/packages/key/key.test.ts b/packages/key/key.test.ts index df832e81..dffc557a 100644 --- a/packages/key/key.test.ts +++ b/packages/key/key.test.ts @@ -1,24 +1,24 @@ // tslint:disable-next-line: no-implicit-dependencies -import { scale } from "@tonaljs/scale"; -import { majorKey, majorTonicFromKeySignature, minorKey } from "./index"; +import { get as scale } from "@tonaljs/scale"; +import Key from "./index"; describe("@tonal/key", () => { test("fromAlter", () => { - expect(majorTonicFromKeySignature("###")).toEqual("A"); - expect(majorTonicFromKeySignature(3)).toEqual("A"); - expect(majorTonicFromKeySignature("b")).toEqual("F"); - expect(majorTonicFromKeySignature("bb")).toEqual("Bb"); - expect(majorTonicFromKeySignature("other")).toEqual(null); + expect(Key.majorTonicFromKeySignature("###")).toEqual("A"); + expect(Key.majorTonicFromKeySignature(3)).toEqual("A"); + expect(Key.majorTonicFromKeySignature("b")).toEqual("F"); + expect(Key.majorTonicFromKeySignature("bb")).toEqual("Bb"); + expect(Key.majorTonicFromKeySignature("other")).toEqual(null); }); test("keySignature", () => { const tonics = "C D E F G A B".split(" "); - expect(tonics.map(tonic => majorKey(tonic).keySignature).join(" ")).toEqual( - " ## #### b # ### #####" - ); + expect( + tonics.map(tonic => Key.majorKey(tonic).keySignature).join(" ") + ).toEqual(" ## #### b # ### #####"); }); describe("scale names", () => { test("natural scales", () => { - const chordScales = minorKey("C").natural.chordScales; + const chordScales = Key.minorKey("C").natural.chordScales; expect(chordScales.map(scale).map(scale => scale.name)).toEqual([ "C aeolian", "D locrian", @@ -30,7 +30,7 @@ describe("@tonal/key", () => { ]); }); test("harmonic scales", () => { - const chordScales = minorKey("C").harmonic.chordScales; + const chordScales = Key.minorKey("C").harmonic.chordScales; expect(chordScales.map(scale).map(scale => scale.name)).toEqual([ "C harmonic minor", "D locrian 6", @@ -42,7 +42,7 @@ describe("@tonal/key", () => { ]); }); test("melodic scales", () => { - const chordScales = minorKey("C").melodic.chordScales; + const chordScales = Key.minorKey("C").melodic.chordScales; expect(chordScales.map(scale).map(scale => scale.name)).toEqual([ "C melodic minor", "D dorian b2", diff --git a/packages/midi/README.md b/packages/midi/README.md index 92914e54..4df41a7f 100644 --- a/packages/midi/README.md +++ b/packages/midi/README.md @@ -2,13 +2,19 @@ > A collection of functions to work with midi numbers. -## Install +## Usage -`npm i --save @tonaljs/midi` +ES6: -or +```js +import { Midi } from "@tonaljs/tonal"; +``` + +nodejs: -`yarn add @tonaljs/midi` +```js +const { Midi } = require("@tonaljs/tonal"); +``` ## API diff --git a/packages/midi/index.ts b/packages/midi/index.ts index f6113840..e37f7808 100644 --- a/packages/midi/index.ts +++ b/packages/midi/index.ts @@ -1,4 +1,4 @@ -import { note as toNote, NoteName } from "@tonaljs/core"; +import { note as props, NoteName } from "@tonaljs/core"; type Midi = number; @@ -24,7 +24,7 @@ export function toMidi(note: NoteName | number): number | null { if (isMidi(note)) { return +note; } - const n = toNote(note); + const n = props(note); return n.empty ? null : n.midi; } @@ -97,3 +97,5 @@ export function midiToNoteName(midi: number, options: ToNoteNameOptions = {}) { const o = Math.floor(midi / 12) - 1; return pc + o; } + +export default { isMidi, toMidi, midiToFreq, midiToNoteName, freqToMidi }; diff --git a/packages/midi/midi.test.ts b/packages/midi/midi.test.ts index 57b88061..28658450 100644 --- a/packages/midi/midi.test.ts +++ b/packages/midi/midi.test.ts @@ -1,48 +1,42 @@ -import { - freqToMidi, - isMidi, - midiToFreq, - midiToNoteName, - toMidi -} from "./index"; +import Midi from "./index"; describe("midi", () => { test("isMidi", () => { - expect(isMidi(100)).toBe(true); + expect(Midi.isMidi(100)).toBe(true); }); test("toMidi", () => { - expect(toMidi(100)).toBe(100); - expect(toMidi("C4")).toBe(60); - expect(toMidi("60")).toBe(60); - expect(toMidi(0)).toBe(0); - expect(toMidi("0")).toBe(0); - expect(toMidi(-1)).toBe(null); - expect(toMidi(128)).toBe(null); - expect(toMidi("blah")).toBe(null); + expect(Midi.toMidi(100)).toBe(100); + expect(Midi.toMidi("C4")).toBe(60); + expect(Midi.toMidi("60")).toBe(60); + expect(Midi.toMidi(0)).toBe(0); + expect(Midi.toMidi("0")).toBe(0); + expect(Midi.toMidi(-1)).toBe(null); + expect(Midi.toMidi(128)).toBe(null); + expect(Midi.toMidi("blah")).toBe(null); }); test("freqToMidi", () => { - expect(freqToMidi(220)).toBe(57); - expect(freqToMidi(261.62)).toBe(60); - expect(freqToMidi(261)).toBe(59.96); + expect(Midi.freqToMidi(220)).toBe(57); + expect(Midi.freqToMidi(261.62)).toBe(60); + expect(Midi.freqToMidi(261)).toBe(59.96); }); test("midiToFreq", () => { - expect(midiToFreq(60)).toEqual(261.6255653005986); - expect(midiToFreq(69, 443)).toEqual(443); + expect(Midi.midiToFreq(60)).toEqual(261.6255653005986); + expect(Midi.midiToFreq(69, 443)).toEqual(443); }); test("midiToNoteName", () => { const notes = [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72]; - expect(notes.map(m => midiToNoteName(m)).join(" ")).toEqual( + expect(notes.map(m => Midi.midiToNoteName(m)).join(" ")).toEqual( "C4 Db4 D4 Eb4 E4 F4 Gb4 G4 Ab4 A4 Bb4 B4 C5" ); expect( - notes.map(n => midiToNoteName(n, { sharps: true })).join(" ") + notes.map(n => Midi.midiToNoteName(n, { sharps: true })).join(" ") ).toEqual("C4 C#4 D4 D#4 E4 F4 F#4 G4 G#4 A4 A#4 B4 C5"); expect( - notes.map(n => midiToNoteName(n, { pitchClass: true })).join(" ") + notes.map(n => Midi.midiToNoteName(n, { pitchClass: true })).join(" ") ).toEqual("C Db D Eb E F Gb G Ab A Bb B C"); }); }); diff --git a/packages/mode/README.md b/packages/mode/README.md index 6d1b207c..8b4c5821 100644 --- a/packages/mode/README.md +++ b/packages/mode/README.md @@ -2,9 +2,23 @@ `@tonaljs/mode` greek modes dictionary +## Usage + +ES6: + +```js +import { Mode } from "@tonaljs/tonal"; +``` + +node: + +```js +const { Mode } = require("@tonaljs/tonal"); +``` + ## API -### `mode(name: string) => Mode` +#### `get(name: string) => object` Given a mode name, returns a Mode object with the following fields: @@ -19,7 +33,7 @@ Given a mode name, returns a Mode object with the following fields: Example: ```js -mode("major"); +Mode.get("major"); // { // name: "ionian", // aliases: ["major"] @@ -32,17 +46,23 @@ mode("major"); // } ``` -### `entries() => Mode[]` +#### `names() => string[]` -Return a list of known modes - -Example: +Get a list of all mode names. ```js -entries().map(mode => mode.name); +Mode.names(); // => ["ionian", "dorian", "phrygian", "lydian", "mixolydian", "aeolian", "locrian"]; ``` +#### `all() => object[]` + +Return a list of known modes + +```js +Mode.all(); +``` + ## How to? #### Get notes from a mode? @@ -50,13 +70,12 @@ entries().map(mode => mode.name); For example, "A major" mode: ```js -import { transpose } from "@tonaljs/core"; -import { mode } from "@tonaljs/mode"; +import { Mode, Note } from "@tonaljs/tonal"; -mode("major").intervals.map(interval => transpose("A", interval)); +Mode.get("major").intervals.map(Note.transposeFrom("A")); ["A", "B", "C#", "D", "E", "F#", "G#"]; ``` ## Want more? -Take a look to [@tonal/key]() +Take a look to [@tonal/key](/packages/key) diff --git a/packages/mode/index.ts b/packages/mode/index.ts index 66faf75b..7a2a1727 100644 --- a/packages/mode/index.ts +++ b/packages/mode/index.ts @@ -1,4 +1,4 @@ -import { Named } from "@tonaljs/core"; +import { deprecate, Named } from "@tonaljs/core"; import { chromaToIntervals, EmptyPcset, Pcset } from "@tonaljs/pcset"; import DATA, { ModeDefinition } from "./data"; @@ -21,9 +21,9 @@ const NoMode: Mode = { aliases: [] }; -const all: Mode[] = DATA.map(toMode); +const modes: Mode[] = DATA.map(toMode); const index: Record = {}; -all.forEach(mode => { +modes.forEach(mode => { index[mode.name] = mode; mode.aliases.forEach(alias => { index[alias] = mode; @@ -36,7 +36,7 @@ type ModeLiteral = string | Named; * Get a Mode by it's name * * @example - * mode('dorian') + * get('dorian') * // => * // { * // intervals: [ '1P', '2M', '3m', '4P', '5P', '6M', '7m' ], @@ -51,19 +51,29 @@ type ModeLiteral = string | Named; * // aliases: [] * // } */ -export function mode(name: ModeLiteral): Mode { +export function get(name: ModeLiteral): Mode { return typeof name === "string" ? index[name.toLowerCase()] || NoMode : name && name.name - ? mode(name.name) + ? get(name.name) : NoMode; } +export const mode = deprecate("Mode.mode", "Mode.get", get); + /** - * Get a list of all know modes + * Get a list of all modes */ -export function entries() { - return all.slice(); +export function all() { + return modes.slice(); +} +export const entries = deprecate("Mode.mode", "Mode.all", all); + +/** + * Get a list of all mode names + */ +export function names() { + return modes.map(mode => mode.name); } function toMode(mode: ModeDefinition): Mode { @@ -85,3 +95,12 @@ function toMode(mode: ModeDefinition): Mode { aliases }; } + +export default { + get, + names, + all, + // deprecated + entries, + mode +}; diff --git a/packages/mode/mode.test.ts b/packages/mode/mode.test.ts index 952b7b71..98d12635 100644 --- a/packages/mode/mode.test.ts +++ b/packages/mode/mode.test.ts @@ -1,11 +1,9 @@ -import { entries, mode } from "./index"; - -const names = entries().map(m => m.name); +import Mode from "./index"; describe("Mode", () => { describe("mode", () => { test("properties", () => { - expect(mode("ionian")).toEqual({ + expect(Mode.get("ionian")).toEqual({ empty: false, modeNum: 0, name: "ionian", @@ -18,40 +16,40 @@ describe("Mode", () => { aliases: ["major"], intervals: ["1P", "2M", "3M", "4P", "5P", "6M", "7M"] }); - expect(mode("major")).toEqual(mode("ionian")); + expect(Mode.get("major")).toEqual(Mode.get("ionian")); }); test("accept Named as parameter", () => { - expect(mode(mode("major"))).toEqual(mode("major")); - expect(mode({ name: "Major" })).toEqual(mode("major")); + expect(Mode.get(Mode.get("major"))).toEqual(Mode.get("major")); + expect(Mode.get({ name: "Major" })).toEqual(Mode.get("major")); }); test("name is case independent", () => { - expect(mode("Dorian")).toEqual(mode("dorian")); + expect(Mode.get("Dorian")).toEqual(Mode.get("dorian")); }); test("setNum", () => { - const pcsets = names.map(name => mode(name).setNum); + const pcsets = Mode.names().map(name => Mode.get(name).setNum); expect(pcsets).toEqual([2773, 2902, 3418, 2741, 2774, 2906, 3434]); }); test("alt", () => { - const alt = names.map(name => mode(name).alt); + const alt = Mode.names().map(name => Mode.get(name).alt); expect(alt).toEqual([0, 2, 4, -1, 1, 3, 5]); }); test("triad", () => { - const triads = names.map(name => mode(name).triad); + const triads = Mode.names().map(name => Mode.get(name).triad); expect(triads).toEqual(["", "m", "m", "", "", "m", "dim"]); }); test("seventh", () => { - const sevenths = names.map(name => mode(name).seventh); + const sevenths = Mode.names().map(name => Mode.get(name).seventh); expect(sevenths).toEqual(["Maj7", "m7", "m7", "Maj7", "7", "m7", "m7b5"]); }); test("aliases", () => { - expect(mode("major")).toEqual(mode("ionian")); - expect(mode("minor")).toEqual(mode("aeolian")); + expect(Mode.get("major")).toEqual(Mode.get("ionian")); + expect(Mode.get("minor")).toEqual(Mode.get("aeolian")); }); }); test("names", () => { - expect(names).toEqual([ + expect(Mode.names()).toEqual([ "ionian", "dorian", "phrygian", diff --git a/packages/note/README.md b/packages/note/README.md index 9a74cd36..f6e586b4 100644 --- a/packages/note/README.md +++ b/packages/note/README.md @@ -2,66 +2,175 @@ > A collection of functions to manipulate musical notes -## API +## Usage + +ES6: -### `tokenize(noteName: string) => [string, string, string, string]` +```js +import { Note } from "@tonaljs/tonal"; +``` -Given a note name, returns its parts: [letter, accidentals, octave, rest]: +nodejs: ```js -tokenize("c##4"); // => ["C", "##", "4", ""] -tokenize("c##4 mixolidian"); // => ["C", "##", "4", " mixolydian"] -tokenize("not a note"); // => ["", "", "", ""] +const { Note } = require("@tonaljs/tonal"); ``` -### `simplify(noteName: string) => string` +## API -Given a note name, return the same note with less accidentals (or "" if not a valid note): +### Note properties + +#### `Note.get(noteName: string) => Note` + +Given a note name, it returns an object with the following properties: + +- name: the note name +- pc: the pitch class name +- letter: the note letter +- step: the letter number (0..6) +- acc: the note accidentals +- alt: the accidental number (..., -1 = 'b', 0 = '', 1 = '#', ...) +- oct: the octave (or null if not present) +- chroma: the note chroma (0..11) +- midi: the note midi or null if octave is not present +- freq: the note frequency in Hertzes, or null if the octave is note present ```js -simplify("C#"); // => "C#" -simplify("C##"); // => "D" -simplify("C###"); // => "D#" +Note.get("C4"); // => { name: "C4", midi: 60, ... } ``` -### `enharmonic(noteName: string) => string` +It has several shorthands to retrieve properties easily: -Given a note name, returns it enharmonic not (or "" if not valid note): +```js +Note.name("fx4"); // => "F##4" +Note.pitchClass("Ab5"); // => "Ab" +Note.accidentals("Eb"); // => 'Eb' +Note.octave("C4"); // => 4 +Note.midi("A4"); // => 6 +Note.freq("A4"); // => 440 +Note.chroma("D"); // => 2 + +["C", "D", "E"].map(Note.chroma); // => [0, 2, 4] +``` + +#### `Note.fromMidi(midi: number) => string` + +Given a midi number, returns the note name. This function is the same as `midiToNoteName` from [@tonaljs/midi](/packages/midi) + +```js +Note.fromMidi(61); // => "Db4" +Note.fromMidi(61.7); // => "D4" +[60, 61, 62].map(Note.fromMidi); // => ["C4", "Db4", "D4"] +``` + +There's also a `Note.fromMidiSharps` version: ```js -enharmonic("C#"); // => "Db" -enharmonic("C##"); // => "D" -enharmonic("C###"); // => "Eb" +Note.fromMidiSharps(61); // => "C#4" ``` -### `transposeBy(interval: string) => (note: string) => string` +### Transposition and distances + +#### `transpose(note: string, interval: string) => string` + +Transpose a note by an interval. It returns the note name or "" if not valid parameters. -Given an interval, returns a function that transposes a note by that interval: +Examples: ```js -["C", "D", "E"].map(transposeBy("5P")); -// => ["G", "A", "B"] +Note.transpose("d3", "3M"); // => "F#3" +Note.transpose("D", "3M"); // => "F#" ``` -### `transposeFrom(note: string) => (interval: string) => string` +`transposeBy` and `transposeFrom` are currified versions of this function to make easy work with arrays: -Given a note, returns a function that transposes that note by given interval: +```js +["C", "D", "E"].map(Note.transposeBy("5P")); +// => ["G", "A", "B"] +``` ```js -["1P", "3M", "5P"].map(transposeFrom("C")); +["1P", "3M", "5P"].map(Note.transposeFrom("C")); // => ["C", "E", "G"] ``` -### `transposeFifths(noteName: string, fifths: number) => string` +#### `transposeFifths(noteName: string, fifths: number) => string` Transpose a note a given number of fifths: ```js -transposeFifths("G4", 3); // => "E6" -transposeFifths("G", 3); // => "E" +Note.transposeFifths("G4", 3); // => "E6" +Note.transposeFifths("G", 3); // => "E" [0, 1, 2, 3, 4, 5, 6].map(n => transposeFifths("F#", n)); // => ["F#", "C#", "G#", "D#", "A#", "E#", "B#"] [0, -1, -2, -3, -4, -5, -6].map(n => transposeFifths("Bb", n)); // => ["Bb", "Eb", "Ab", "Db", "Gb", "Cb", "Fb"] ``` + +#### `Note.distance(from: string, to: string) => string` + +Find interval between notes: + +```js +``` + +### Names collections + +#### `names(array?: any[]) => string[]` + +Get note names of an array of anything. Notice that names are normalized: + +```js +Note.names(["fx", "bb", 12, "nothing", {}, null])) // => ["F##", "Bb"]; +``` + +Without parameters, it returns a list of natural pitch classes: + +```js +Note.names(); // =>["C", "D", "E", "F", "G", "A", "B"] +``` + +#### `sortedNames(array?: any[], comparator?: NoteComparator) => string[]` + +Sort an array of note names in ascending order. Pitch classes are listed before notes. Anything that is not a note is removed: + +```js +Note.sortedNames(["c2", "c5", "c1", "c0", "c6", "c"]); +// => ['C', 'C0', 'C1', 'C2', 'C5', 'C6'] +Note.sortedNames(["c", "F", "G", "a", "b", "h", "J"]); +// => ['C', 'F', 'G', 'A', 'B'] +``` + +An optional comparator can be passed as a second argument: + +```js +Note.sortedNames(["c2", "c5", "c1", "c0", "c6", "c"], Note.descending); +// => ['C6', 'C5', 'C2', 'C3', 'C1', 'C0'] +``` + +#### `sortedUniqNames(array?: any[]) => string[]` + +Sort notes ascending and remove duplicates. + +### Enharmonics + +#### `simplify(noteName: string) => string` + +Given a note name, return the same note with less accidentals (or "" if not a valid note): + +```js +Note.simplify("C#"); // => "C#" +Note.simplify("C##"); // => "D" +Note.simplify("C###"); // => "D#" +``` + +#### `enharmonic(noteName: string) => string` + +Given a note name, returns it enharmonic not (or "" if not valid note): + +```js +Note.enharmonic("C#"); // => "Db" +Note.enharmonic("C##"); // => "D" +Note.enharmonic("C###"); // => "Eb" +``` diff --git a/packages/note/index.ts b/packages/note/index.ts index 6274de42..12acf2ba 100644 --- a/packages/note/index.ts +++ b/packages/note/index.ts @@ -1,75 +1,147 @@ import { coordToNote, IntervalName, - note, + Named, + Note, + note as props, + NoteLiteral, NoteName, Pitch, - transpose + transpose as _tr } from "@tonaljs/core"; -export { tokenizeNote as tokenize } from "@tonaljs/core"; import { midiToNoteName } from "@tonaljs/midi"; -const toNoteName = (sameAccidentals: boolean) => ( - noteName: NoteName | Pitch -): string => { - const n = note(noteName); - if (n.empty) { - return ""; +const NAMES = ["C", "D", "E", "F", "G", "A", "B"]; + +const toName = (n: Named) => n.name; +const onlyNotes = (array: any[]) => + array.map(props).filter(n => !n.empty) as Note[]; + +/** + * Return the natural note names without octave + * @function + * @example + * Note.names(); // => ["C", "D", "E", "F", "G", "A", "B"] + */ +export function names(array?: any[]): string[] { + if (array === undefined) { + return NAMES.slice(); + } else if (!Array.isArray(array)) { + return []; + } else { + return onlyNotes(array).map(toName); } - const sharps = sameAccidentals ? n.alt > 0 : n.alt < 0; - const pitchClass = n.midi === null; - return midiToNoteName(n.midi || n.chroma, { sharps, pitchClass }); -}; +} /** - * Simplify a note + * Get a note from a note name * * @function - * @param {string} note - the note to be simplified - * - sameAccType: default true. Use same kind of accidentals that source - * @return {string} the simplfied note or '' if not valid note * @example - * simplify("C##") // => "D" - * simplify("C###") // => "D#" - * simplify("C###") - * simplify("B#4") // => "C5" + * Note.get('Bb4') // => { name: "Bb4", midi: 70, chroma: 10, ... } */ -export const simplify = toNoteName(true); +export const get = props; /** - * Get enharmonic of a note + * Get the note name + * @function + */ +export const name = (note: NoteLiteral) => get(note).name; + +/** + * Get the note pitch class name + * @function + */ +export const pitchClass = (note: NoteLiteral) => get(note).pc; + +/** + * Get the note accidentals + * @function + */ +export const accidentals = (note: NoteLiteral) => get(note).acc; + +/** + * Get the note octave + * @function + */ +export const octave = (note: NoteLiteral) => get(note).oct; + +/** + * Get the note midi + * @function + */ +export const midi = (note: NoteLiteral) => get(note).midi; + +/** + * Get the note midi + * @function + */ +export const freq = (note: NoteLiteral) => get(note).freq; + +/** + * Get the note chroma + * @function + */ +export const chroma = (note: NoteLiteral) => get(note).chroma; + +/** + * Given a midi number, returns a note name. Uses flats for altered notes. * * @function - * @param {string} note - * @return {string} the enhramonic note or '' if not valid note + * @param {number} midi - the midi note number + * @return {string} the note name * @example - * Note.enharmonic("Db") // => "C#" - * Note.enhramonic("C") // => "C" + * Note.fromMidi(61) // => "Db4" + * Note.fromMidi(61.7) // => "D4" */ -export const enharmonic = toNoteName(false); +export function fromMidi(midi: number) { + return midiToNoteName(midi); +} /** - * Transpose by an interval + * Given a midi number, returns a note name. Uses flats for altered notes. + * + * @function + * @param {number} midi - the midi note number + * @return {string} the note name + * @example + * Note.fromMidiSharps(61) // => "C#4" + */ + +export function fromMidiSharps(midi: number) { + return midiToNoteName(midi, { sharps: true }); +} + +/** + * Transpose a note by an interval + */ +export const transpose = _tr; +export const tr = _tr; + +/** + * Transpose by an interval. * @function * @param {string} interval * @return {function} a function that transposes by the given interval * @example - * ["C", "D", "E"].map(transposeBy("5P")); + * ["C", "D", "E"].map(Note.transposeBy("5P")); * // => ["G", "A", "B"] */ export const transposeBy = (interval: IntervalName) => (note: NoteName) => transpose(note, interval); +export const trBy = transposeBy; /** * Transpose from a note * @function * @param {string} note * @return {function} a function that transposes the the note by an interval - * ["1P", "3M", "5P"].map(transposeFrom("C")); + * ["1P", "3M", "5P"].map(Note.transposeFrom("C")); * // => ["C", "E", "G"] */ export const transposeFrom = (note: NoteName) => (interval: IntervalName) => transpose(note, interval); +export const trFrom = transposeFrom; /** * Transpose a note by a number of perfect fifths. @@ -85,11 +157,11 @@ export const transposeFrom = (note: NoteName) => (interval: IntervalName) => * [0, 1, 2, 3, 4].map(fifths => transposeFifths("C", fifths)) // => ["C", "G", "D", "A", "E"] */ export function transposeFifths(noteName: NoteName, fifths: number): NoteName { - const n = note(noteName); - if (n.empty) { + const note = get(noteName); + if (note.empty) { return ""; } - const [nFifths, nOcts] = n.coord; + const [nFifths, nOcts] = note.coord; const transposed = nOcts === undefined ? coordToNote([nFifths + fifths]) @@ -97,3 +169,92 @@ export function transposeFifths(noteName: NoteName, fifths: number): NoteName { return transposed.name; } +export const trFifths = transposeFifths; + +export type NoteComparator = (a: Note, b: Note) => number; + +export const ascending: NoteComparator = (a, b) => a.height - b.height; +export const descending: NoteComparator = (a, b) => b.height - a.height; + +export function sortedNames( + notes: any[], + comparator?: NoteComparator +): string[] { + comparator = comparator || ascending; + return onlyNotes(notes) + .sort(comparator) + .map(toName); +} + +export function sortedUniqNames(notes: any[]): string[] { + return sortedNames(notes, ascending).filter( + (n, i, a) => i === 0 || n !== a[i - 1] + ); +} + +/** + * Simplify a note + * + * @function + * @param {string} note - the note to be simplified + * - sameAccType: default true. Use same kind of accidentals that source + * @return {string} the simplified note or '' if not valid note + * @example + * simplify("C##") // => "D" + * simplify("C###") // => "D#" + * simplify("C###") + * simplify("B#4") // => "C5" + */ +export const simplify = nameBuilder(true); + +/** + * Get enharmonic of a note + * + * @function + * @param {string} note + * @return {string} the enharmonic note or '' if not valid note + * @example + * Note.enharmonic("Db") // => "C#" + * Note.enharmonic("C") // => "C" + */ +export const enharmonic = nameBuilder(false); + +function nameBuilder(sameAccidentals: boolean) { + return (noteName: NoteName | Pitch): string => { + const note = get(noteName); + if (note.empty) { + return ""; + } + const sharps = sameAccidentals ? note.alt > 0 : note.alt < 0; + const pitchClass = note.midi === null; + return midiToNoteName(note.midi || note.chroma, { sharps, pitchClass }); + }; +} + +export default { + names, + get, + name, + pitchClass, + accidentals, + octave, + midi, + ascending, + descending, + sortedNames, + sortedUniqNames, + fromMidi, + fromMidiSharps, + freq, + chroma, + transpose, + tr, + transposeBy, + trBy, + transposeFrom, + trFrom, + transposeFifths, + trFifths, + simplify, + enharmonic +}; diff --git a/packages/note/note.test.ts b/packages/note/note.test.ts index 79f26823..7e32d8a8 100644 --- a/packages/note/note.test.ts +++ b/packages/note/note.test.ts @@ -1,52 +1,118 @@ -import { - enharmonic, - simplify, - transposeBy, - transposeFifths, - transposeFrom -} from "./index"; +import Note from "./index"; const $ = (str: string) => str.split(" "); describe("note", () => { + test("get", () => { + expect(Note.get("C4")).toEqual({ + acc: "", + alt: 0, + chroma: 0, + coord: [0, 4], + empty: false, + freq: 261.6255653005986, + height: 60, + letter: "C", + midi: 60, + name: "C4", + oct: 4, + pc: "C", + step: 0 + }); + expect(Note.get("C4")).toEqual(Note.get(Note.get("C4"))); + }); + test("property shorthands", () => { + expect(Note.name("db")).toEqual("Db"); + expect(Note.pitchClass("Ax4")).toEqual("A##"); + expect(Note.chroma("db4")).toEqual(1); + expect(Note.midi("db4")).toEqual(61); + expect(Note.freq("A4")).toEqual(440); + }); test("simplify", () => { - expect(simplify("C#")).toEqual("C#"); - expect(simplify("C##")).toEqual("D"); - expect(simplify("C###")).toEqual("D#"); - expect(simplify("B#4")).toEqual("C5"); + expect(Note.simplify("C#")).toEqual("C#"); + expect(Note.simplify("C##")).toEqual("D"); + expect(Note.simplify("C###")).toEqual("D#"); + expect(Note.simplify("B#4")).toEqual("C5"); const notes = $("C## C### F##4 Gbbb5 B#4 Cbb4"); - expect(notes.map(simplify)).toEqual($("D D# G4 E5 C5 Bb3")); - expect(simplify("x")).toEqual(""); + expect(notes.map(Note.simplify)).toEqual($("D D# G4 E5 C5 Bb3")); + expect(Note.simplify("x")).toEqual(""); + }); + + test("from midi", () => { + expect(Note.fromMidi(70)).toEqual("Bb4"); + expect([60, 61, 62].map(Note.fromMidi)).toEqual(["C4", "Db4", "D4"]); + expect([60, 61, 62].map(Note.fromMidiSharps)).toEqual(["C4", "C#4", "D4"]); + }); + + test("names", () => { + expect(Note.names()).toEqual(["C", "D", "E", "F", "G", "A", "B"]); + expect(Note.names(["fx", "bb", 12, "nothing", {}, null])).toEqual([ + "F##", + "Bb" + ]); + }); + test("sortedNames", () => { + expect(Note.sortedNames($("c f g a b h j"))).toEqual($("C F G A B")); + expect(Note.sortedNames($("c f g a b h j j h b a g f c"))).toEqual( + $("C C F F G G A A B B") + ); + expect(Note.sortedNames($("c2 c5 c1 c0 c6 c"))).toEqual( + $("C C0 C1 C2 C5 C6") + ); + expect(Note.sortedNames($("c2 c5 c1 c0 c6 c"), Note.descending)).toEqual( + $("C6 C5 C2 C1 C0 C") + ); + }); + + test("sortedUniq", () => { + expect(Note.sortedUniqNames($("a b c2 1p p2 c2 b c c3"))).toEqual( + $("C A B C2 C3") + ); + }); + + test("transpose", () => { + expect(Note.transpose("A4", "3M")).toEqual("C#5"); + expect(Note.tr("A4", "3M")).toEqual("C#5"); }); test("transposeFrom", () => { - expect(transposeFrom("C4")("5P")).toEqual("G4"); - expect(["1P", "3M", "5P"].map(transposeFrom("C"))).toEqual(["C", "E", "G"]); + expect(Note.transposeFrom("C4")("5P")).toEqual("G4"); + expect(["1P", "3M", "5P"].map(Note.transposeFrom("C"))).toEqual([ + "C", + "E", + "G" + ]); }); test("transposeBy", () => { - expect(transposeBy("5P")("C4")).toEqual("G4"); - expect(["C", "D", "E"].map(transposeBy("5P"))).toEqual(["G", "A", "B"]); + expect(Note.transposeBy("5P")("C4")).toEqual("G4"); + expect(["C", "D", "E"].map(Note.transposeBy("5P"))).toEqual([ + "G", + "A", + "B" + ]); }); test("enharmonic", () => { - expect(enharmonic("C#")).toEqual("Db"); - expect(enharmonic("C##")).toEqual("D"); - expect(enharmonic("C###")).toEqual("Eb"); - expect(enharmonic("B#4")).toEqual("C5"); + expect(Note.enharmonic("C#")).toEqual("Db"); + expect(Note.enharmonic("C##")).toEqual("D"); + expect(Note.enharmonic("C###")).toEqual("Eb"); + expect(Note.enharmonic("B#4")).toEqual("C5"); const notes = $("C## C### F##4 Gbbb5 B#4 Cbb4"); - expect(notes.map(enharmonic)).toEqual($("D Eb G4 E5 C5 A#3")); - expect(enharmonic("x")).toEqual(""); + expect(notes.map(Note.enharmonic)).toEqual($("D Eb G4 E5 C5 A#3")); + expect(Note.enharmonic("x")).toEqual(""); }); test("transposeFifths", () => { - expect(transposeFifths("G4", 3)).toEqual("E6"); - expect(transposeFifths("G", 3)).toEqual("E"); - const ns = [0, 1, 2, 3, 4, 5].map(n => transposeFifths("C2", n)); + expect(Note.transposeFifths("G4", 3)).toEqual("E6"); + expect(Note.transposeFifths("G", 3)).toEqual("E"); + const ns = [0, 1, 2, 3, 4, 5].map(n => Note.transposeFifths("C2", n)); expect(ns).toEqual(["C2", "G2", "D3", "A3", "E4", "B4"]); - const sharps = [0, 1, 2, 3, 4, 5, 6].map(n => transposeFifths("F#", n)); + const sharps = [0, 1, 2, 3, 4, 5, 6].map(n => + Note.transposeFifths("F#", n) + ); expect(sharps).toEqual(["F#", "C#", "G#", "D#", "A#", "E#", "B#"]); const flats = [0, -1, -2, -3, -4, -5, -6].map(n => - transposeFifths("Bb", n) + Note.transposeFifths("Bb", n) ); expect(flats).toEqual(["Bb", "Eb", "Ab", "Db", "Gb", "Cb", "Fb"]); }); diff --git a/packages/pcset/README.md b/packages/pcset/README.md index 1b89528c..47d7c5ec 100644 --- a/packages/pcset/README.md +++ b/packages/pcset/README.md @@ -2,13 +2,27 @@ > Functions to create and manipulate musical pitch class sets -A pitch class set is a set (no repeated) of pitch classes (notes without octaves). Pitch classes are usefull to identify musical structures (like if two chords are related) +A pitch class set is a set (no repeated) of pitch classes (notes without octaves). Pitch classes are useful to identify musical structures (if two chords are related, for example) + +## Usage + +ES6: + +```js +import { Pcset } from "@tonaljs/tonal"; +``` + +nodejs: + +```js +const { Pcset } = require("@tonaljs/tonal"); +``` ## API -### `pcset(src: note[] | string | number) => Pcset` +#### `get(src: note[] | string | number)` -Given a collection of notes, a pitch class chroma string or a pitch class number, it returns the Pcset data object with the following attributes: +Given a collection of notes, a pitch class chroma string or a pitch class number, it returns a properties object with the following attributes: - num: the set number. Each pitch class set can be represented by an unique name between 0 and 4096. Those are the possible combinations of 12 different elements (pitch classesj) - chroma: the set number as binary string @@ -18,7 +32,7 @@ Given a collection of notes, a pitch class chroma string or a pitch class number Example: ```js -pcset(["c", "d", "e"]); +Pcset.get(["c", "d", "e"]); // => // { // num: 2688, @@ -28,28 +42,63 @@ pcset(["c", "d", "e"]); // } ``` -It is possible to obtain the Pcset object from chroma or number: +It is possible to obtain the properties from chroma or set number. All this function calls returns the same object: + +```js +Pcset.get(["c", "d", "e"]); +Pcset.get(2688); +Pcset.get("101010000000"); +``` + +Several shorthands (`num`, `chroma`, intervals`) are provided: + +```js +Pcset.chroma(["c", "d", "e"]); //=> "101010000000" +Pcset.num(["c", "d", "e"]); //=> 2192 + +// several set representations are accepted +Pcset.chroma(2192); //=> "101010000000" +Pcset.num("101010000000"); // => 2192 +``` + +Intervals are always calculated from `C`: + +```js +Pcset.intervals(["c", "d", "e"]); // => ["1P", "5P", "7M"] +Pcset.intervals(["D", "F", "A"]); // => ["2M", "4P", "6M"] +``` + +### `isIncludedIn(parent: Set) => (note: string) => boolean` + +Test if a note is included in the given set. This function is currified: + +```js +const isInCTriad = isNoteIncludedIn(["C", "E", "G"]); +isInCTriad("C4"); // => true +isInCTriad("C#4"); // => false +``` + +Keep in mind that enharmonics are included: ```js -pcset(["c", "d", "e"]) == pcset(2688); -pcset(2688) == pcset("101010000000"); +isInCTriad("Fb"); // => true ``` -### `isSubsetOf(parent: Set) => (subset: Set) => boolean` +#### `isSubsetOf(parent: Set) => (subset: Set) => boolean` Test if a set is a subset of another. This function is currified -### `isSupersetOf(subset: Set) => (parent: Set) => boolean` +#### `isSupersetOf(subset: Set) => (parent: Set) => boolean` Test if a set is a superset of another. This function is currified ## Want more? -Take a look to [@tonal/scale-dictionary]() or [@tonal/chord-dictionary]() that are, in fact, dictionaries of pitch class sets. +Take a look to [@tonal/scale-type]() or [@tonal/chord-type]() that are, in fact, dictionaries of pitch class sets. ## FAQ -#### How do I get a list of all possible music scales? +##### How do I get a list of all possible music scales? ```js import { chromas, pcset } from "@tonaljs/pcset"; diff --git a/packages/pcset/index.ts b/packages/pcset/index.ts index 614a090b..e96a5fb5 100644 --- a/packages/pcset/index.ts +++ b/packages/pcset/index.ts @@ -1,5 +1,6 @@ -import { compact, range, rotate } from "@tonaljs/array"; +import { compact, range, rotate } from "@tonaljs/collection"; import { + deprecate, Interval, interval, IntervalName, @@ -73,7 +74,7 @@ export type Set = /** * Get the pitch class set of a collection of notes or set number or chroma */ -export function pcset(src: Set): Pcset { +export function get(src: Set): Pcset { const chroma: PcsetChroma = isChroma(src) ? src : isPcsetNum(src) @@ -87,7 +88,51 @@ export function pcset(src: Set): Pcset { return (cache[chroma] = cache[chroma] || chromaToPcset(chroma)); } -const IVLS = "1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M".split(" "); +/** + * Use Pcset.properties + * @function + * @deprecated + */ +export const pcset = deprecate("Pcset.pcset", "Pcset.get", get); + +/** + * Get pitch class set chroma + * @function + * @example + * Pcset.chroma(["c", "d", "e"]); //=> "101010000000" + */ +const chroma = (set: Set) => get(set).chroma; + +/** + * Get intervals (from C) of a set + * @function + * @example + * Pcset.intervals(["c", "d", "e"]); //=> + */ +const intervals = (set: Set) => get(set).intervals; + +/** + * Get pitch class set number + * @function + * @example + * Pcset.num(["c", "d", "e"]); //=> 2192 + */ +const num = (set: Set) => get(set).setNum; + +const IVLS = [ + "1P", + "2m", + "2M", + "3m", + "3M", + "4P", + "5d", + "5P", + "6m", + "6M", + "7m", + "7M" +]; /** * @private @@ -105,8 +150,6 @@ export function chromaToIntervals(chroma: PcsetChroma): IntervalName[] { return intervals; } -let all: PcsetChroma[]; - /** * Get a list of all possible pitch class sets (all possible chromas) *having * C as root*. There are 2048 different chromas. If you want them with another @@ -116,8 +159,7 @@ let all: PcsetChroma[]; * @return {Array} an array of possible chromas from '10000000000' to '11111111111' */ export function chromas(): PcsetChroma[] { - all = all || range(2048, 4095).map(setNumToChroma); - return all.slice(); + return range(2048, 4095).map(setNumToChroma); } /** @@ -135,7 +177,7 @@ export function chromas(): PcsetChroma[] { * Pcset.modes(["C", "D", "E"]).map(Pcset.intervals) */ export function modes(set: Set, normalize = true): PcsetChroma[] { - const pcs = pcset(set); + const pcs = get(set); const binary = pcs.chroma.split(""); return compact( @@ -156,7 +198,7 @@ export function modes(set: Set, normalize = true): PcsetChroma[] { * Pcset.isEqual(["c2", "d3"], ["c5", "d2"]) // => true */ export function isEqual(s1: Set, s2: Set) { - return pcset(s1).setNum === pcset(s2).setNum; + return get(s1).setNum === get(s2).setNum; } /** @@ -175,10 +217,10 @@ export function isEqual(s1: Set, s2: Set) { * inCMajor(["e6", "c4", "d3"]) // => false */ export function isSubsetOf(set: Set) { - const s = pcset(set).setNum; + const s = get(set).setNum; return (notes: Set | Pcset) => { - const o = pcset(notes).setNum; + const o = get(notes).setNum; // tslint:disable-next-line: no-bitwise return s && s !== o && (o & s) === o; }; @@ -197,9 +239,9 @@ export function isSubsetOf(set: Set) { * extendsCMajor(["c6", "e4", "g3"]) // => false */ export function isSupersetOf(set: Set) { - const s = pcset(set).setNum; + const s = get(set).setNum; return (notes: Set) => { - const o = pcset(notes).setNum; + const o = get(notes).setNum; // tslint:disable-next-line: no-bitwise return s && s !== o && (o | s) === o; }; @@ -215,12 +257,12 @@ export function isSupersetOf(set: Set) { * Can be partially applied * * @example - * const isNoteInCMajor = isNoteIncludedInSet(['C', 'E', 'G']) + * const isNoteInCMajor = isNoteIncludedIn(['C', 'E', 'G']) * isNoteInCMajor('C4') // => true * isNoteInCMajor('C#4') // => false */ -export function isNoteIncludedInSet(set: Set) { - const s = pcset(set); +export function isNoteIncludedIn(set: Set) { + const s = get(set); return (noteName: NoteName): boolean => { const n = note(noteName); @@ -229,7 +271,7 @@ export function isNoteIncludedInSet(set: Set) { } /** @deprecated use: isNoteIncludedIn */ -export const includes = isNoteIncludedInSet; +export const includes = isNoteIncludedIn; /** * Filter a list with a pitch class set @@ -243,13 +285,29 @@ export const includes = isNoteIncludedInSet; * Pcset.filter(["C2"], ["c2", "c#2", "d2", "c3", "c#3", "d3"]) // => [ "c2", "c3" ]) */ export function filter(set: Set) { - const isIncluded = isNoteIncludedInSet(set); + const isIncluded = isNoteIncludedIn(set); return (notes: NoteName[]) => { return notes.filter(isIncluded); }; } -// PRIVATE // +export default { + get, + chroma, + num, + intervals, + chromas, + isSupersetOf, + isSubsetOf, + isNoteIncludedIn, + isEqual, + filter, + modes, + // deprecated + pcset +}; + +//// PRIVATE //// function chromaRotations(chroma: string): string[] { const binary = chroma.split(""); diff --git a/packages/pcset/package.json b/packages/pcset/package.json index 4ed8d342..db6209ad 100644 --- a/packages/pcset/package.json +++ b/packages/pcset/package.json @@ -16,7 +16,7 @@ ], "types": "dist/pcset/index.d.ts", "dependencies": { - "@tonaljs/array": "^3.2.4", + "@tonaljs/collection": "^3.2.4", "@tonaljs/core": "^3.3.0" }, "author": "danigb@gmail.com", diff --git a/packages/pcset/pcset.test.ts b/packages/pcset/pcset.test.ts index 181a3de7..1d3636c3 100644 --- a/packages/pcset/pcset.test.ts +++ b/packages/pcset/pcset.test.ts @@ -1,21 +1,11 @@ -import { - chromas, - EmptyPcset, - filter, - isEqual, - isNoteIncludedInSet, - isSubsetOf, - isSupersetOf, - modes, - pcset -} from "./index"; +import Pcset from "./index"; const $ = (str: string) => str.split(" "); describe("@tonaljs/pcset", () => { describe("pcset", () => { test("from note list", () => { - expect(pcset(["c", "d", "e"])).toEqual({ + expect(Pcset.get(["c", "d", "e"])).toEqual({ empty: false, name: "", setNum: 2688, @@ -23,109 +13,114 @@ describe("@tonaljs/pcset", () => { normalized: "100000001010", intervals: ["1P", "2M", "3M"] }); - expect(pcset(["d", "e", "c"])).toEqual(pcset(["c", "d", "e"])); - expect(pcset(["not a note or interval"])).toEqual(EmptyPcset); - expect(pcset([])).toEqual(EmptyPcset); + expect(Pcset.get(["d", "e", "c"])).toEqual(Pcset.get(["c", "d", "e"])); + expect(Pcset.get(["not a note or interval"]).empty).toBe(true); + expect(Pcset.get([]).empty).toBe(true); }); test("from pcset number", () => { - expect(pcset(2048)).toEqual(pcset(["C"])); + expect(Pcset.get(2048)).toEqual(Pcset.get(["C"])); }); - test("setNum", () => { - expect(pcset("000000000001").setNum).toBe(1); - expect(pcset(["B"]).setNum).toBe(1); - expect(pcset(["Cb"]).setNum).toBe(1); - expect(pcset(["C"]).setNum).toBe(2048); - expect(pcset("100000000000").setNum).toBe(2048); - expect(pcset("111111111111").setNum).toBe(4095); + test("num", () => { + expect(Pcset.num("000000000001")).toBe(1); + expect(Pcset.num(["B"])).toBe(1); + expect(Pcset.num(["Cb"])).toBe(1); + expect(Pcset.num(["C", "E", "G"])).toBe(2192); + expect(Pcset.num(["C"])).toBe(2048); + expect(Pcset.num("100000000000")).toBe(2048); + expect(Pcset.num("111111111111")).toBe(4095); }); test("normalized", () => { - const likeC = pcset(["C"]).chroma; // 100000000000 + const likeC = Pcset.get(["C"]).chroma; // 100000000000 "cdefgab".split("").forEach(pc => { - expect(pcset([pc]).normalized).toBe(likeC); + expect(Pcset.get([pc]).normalized).toBe(likeC); }); - expect(pcset(["E", "F#"]).normalized).toBe(pcset(["C", "D"]).normalized); + expect(Pcset.get(["E", "F#"]).normalized).toBe( + Pcset.get(["C", "D"]).normalized + ); }); }); + test("chroma", () => { - expect(pcset(["C"]).chroma).toBe("100000000000"); - expect(pcset(["D"]).chroma).toBe("001000000000"); - expect(pcset($("c d e")).chroma).toBe("101010000000"); - expect(pcset($("g g#4 a bb5")).chroma).toBe("000000011110"); - expect(pcset($("P1 M2 M3 P4 P5 M6 M7")).chroma).toBe( - pcset($("c d e f g a b")).chroma + expect(Pcset.chroma(["C"])).toBe("100000000000"); + expect(Pcset.chroma(["D"])).toBe("001000000000"); + 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("101010101010").chroma).toBe("101010101010"); - expect(pcset(["one", "two"]).chroma).toBe("000000000000"); - expect(pcset("A B C").chroma).toBe("000000000000"); + expect(Pcset.chroma("101010101010")).toBe("101010101010"); + expect(Pcset.chroma(["one", "two"])).toBe("000000000000"); + expect(Pcset.chroma("A B C")).toBe("000000000000"); }); test("chromas", () => { - expect(chromas().length).toBe(2048); - expect(chromas()[0]).toBe("100000000000"); - expect(chromas()[2047]).toBe("111111111111"); + expect(Pcset.chromas().length).toBe(2048); + expect(Pcset.chromas()[0]).toBe("100000000000"); + expect(Pcset.chromas()[2047]).toBe("111111111111"); }); test("intervals", () => { - expect(pcset("101010101010").intervals).toEqual($("1P 2M 3M 5d 6m 7m")); - expect(pcset("1010").intervals).toEqual([]); - expect(pcset(["D", "F", "A"]).intervals).toEqual(["2M", "4P", "6M"]); + expect(Pcset.intervals("101010101010")).toEqual($("1P 2M 3M 5d 6m 7m")); + expect(Pcset.intervals("1010")).toEqual([]); + expect(Pcset.intervals(["C", "G", "B"])).toEqual(["1P", "5P", "7M"]); + expect(Pcset.intervals(["D", "F", "A"])).toEqual(["2M", "4P", "6M"]); }); test("isChroma", () => { - expect(pcset("101010101010").chroma).toBe("101010101010"); - expect(pcset("1010101").chroma).toBe("000000000000"); - expect(pcset("blah").chroma).toBe("000000000000"); - expect(pcset("c d e").chroma).toBe("000000000000"); + expect(Pcset.get("101010101010").chroma).toBe("101010101010"); + expect(Pcset.get("1010101").chroma).toBe("000000000000"); + expect(Pcset.get("blah").chroma).toBe("000000000000"); + expect(Pcset.get("c d e").chroma).toBe("000000000000"); }); test("isSubsetOf", () => { - const isInCMajor = isSubsetOf($("c4 e6 g")); + const isInCMajor = Pcset.isSubsetOf($("c4 e6 g")); expect(isInCMajor($("c2 g7"))).toBe(true); expect(isInCMajor($("c2 e"))).toBe(true); expect(isInCMajor($("c2 e3 g4"))).toBe(false); expect(isInCMajor($("c2 e3 b5"))).toBe(false); - expect(isSubsetOf($("c d e"))(["C", "D"])).toBe(true); + expect(Pcset.isSubsetOf($("c d e"))(["C", "D"])).toBe(true); }); test("isSubsetOf with chroma", () => { - const isSubset = isSubsetOf("101010101010"); + const isSubset = Pcset.isSubsetOf("101010101010"); expect(isSubset("101000000000")).toBe(true); expect(isSubset("111000000000")).toBe(false); }); test("isSupersetOf", () => { - const extendsCMajor = isSupersetOf(["c", "e", "g"]); + const extendsCMajor = Pcset.isSupersetOf(["c", "e", "g"]); expect(extendsCMajor($("c2 g3 e4 f5"))).toBe(true); expect(extendsCMajor($("e c g"))).toBe(false); expect(extendsCMajor($("c e f"))).toBe(false); - expect(isSupersetOf(["c", "d"])(["c", "d", "e"])).toBe(true); + expect(Pcset.isSupersetOf(["c", "d"])(["c", "d", "e"])).toBe(true); }); test("isSupersetOf with chroma", () => { - const isSuperset = isSupersetOf("101000000000"); + const isSuperset = Pcset.isSupersetOf("101000000000"); expect(isSuperset("101010101010")).toBe(true); expect(isSuperset("110010101010")).toBe(false); }); test("isEqual", () => { - expect(isEqual($("c2 d3 e7 f5"), $("c4 c d5 e6 f1"))).toBeTruthy(); - expect(isEqual($("c f"), $("c4 c f1"))).toBeTruthy(); + expect(Pcset.isEqual($("c2 d3 e7 f5"), $("c4 c d5 e6 f1"))).toBeTruthy(); + expect(Pcset.isEqual($("c f"), $("c4 c f1"))).toBeTruthy(); }); - test("includes", () => { - const isIncludedInC = isNoteIncludedInSet(["c", "d", "e"]); + test("isNoteIncludedIn", () => { + const isIncludedInC = Pcset.isNoteIncludedIn(["c", "d", "e"]); expect(isIncludedInC("C4")).toBe(true); expect(isIncludedInC("C#4")).toBe(false); }); test("filter", () => { - const inCMajor = filter($("c d e")); + const inCMajor = Pcset.filter($("c d e")); expect(inCMajor($("c2 c#2 d2 c3 c#3 d3"))).toEqual($("c2 d2 c3 d3")); - expect(filter($("c"))($("c2 c#2 d2 c3 c#3 d3"))).toEqual($("c2 c3")); + expect(Pcset.filter($("c"))($("c2 c#2 d2 c3 c#3 d3"))).toEqual($("c2 c3")); }); test("modes", () => { - expect(modes($("c d e f g a b"))).toEqual([ + expect(Pcset.modes($("c d e f g a b"))).toEqual([ "101011010101", "101101010110", "110101011010", @@ -134,7 +129,7 @@ describe("@tonaljs/pcset", () => { "101101011010", "110101101010" ]); - expect(modes($("c d e f g a b"), false)).toEqual([ + expect(Pcset.modes($("c d e f g a b"), false)).toEqual([ "101011010101", "010110101011", "101101010110", @@ -148,6 +143,6 @@ describe("@tonaljs/pcset", () => { "011010110101", "110101101010" ]); - expect(modes(["blah", "bleh"])).toEqual([]); + expect(Pcset.modes(["blah", "bleh"])).toEqual([]); }); }); diff --git a/packages/progression/README.md b/packages/progression/README.md index c532ebe1..5e334629 100644 --- a/packages/progression/README.md +++ b/packages/progression/README.md @@ -2,27 +2,41 @@ > Convert chord list to roman numerals analysis chord and reverse -## Resources +## Usage -- [Roman numeral analisys](https://en.wikipedia.org/wiki/Roman_numeral_analysis) -- [Leadsheet chord symbols](https://en.wikipedia.org/wiki/Lead_sheet) +ES6: + +```js +import { Progression } from "@tonaljs/tonal"; +``` + +node: + +```js +const { Progression } = require("@tonaljs/tonal"); +``` ## API -### `fromRomanNumerals(keyTonic: string, chordProgression: string[]) => string[]` +### `Progression.fromRomanNumerals(keyTonic: string, chordProgression: string[]) => string[]` Given a tonic and a chord progression expressed in roman numeral analisys chords, returns the progression expressed in leadsheet chords. ```js -fromRomanNumerals("C", ["IMaj7", "IIm7", "V7"]); +Progression.fromRomanNumerals("C", ["IMaj7", "IIm7", "V7"]); // => ["CMaj7", "Dm7", "G7"] ``` -### `toRomanNumerals(keyTonic: string, chordProgression: string[]) => string[]` +### `Progression.toRomanNumerals(keyTonic: string, chordProgression: string[]) => string[]` The opposite of `fromRomanNumerals`. Given a tonic and a chord progression expressed in leadsheet chords, returns the progression using roman numeral analysis chords. ```js -toRomanNumerals("C", ["CMaj7", "Dm7", "G7"]); +Progression.toRomanNumerals("C", ["CMaj7", "Dm7", "G7"]); // => "IMaj7", "IIm7", "V7"] ``` + +## Resources + +- [Roman numeral analisys](https://en.wikipedia.org/wiki/Roman_numeral_analysis) +- [Leadsheet chord symbols](https://en.wikipedia.org/wiki/Lead_sheet) diff --git a/packages/progression/index.ts b/packages/progression/index.ts index e02bd45f..f69cc53f 100644 --- a/packages/progression/index.ts +++ b/packages/progression/index.ts @@ -1,6 +1,6 @@ import { tokenize } from "@tonaljs/chord"; import { distance, interval, NoteLiteral, transpose } from "@tonaljs/core"; -import { romanNumeral } from "@tonaljs/roman-numeral"; +import { get as romanNumeral } from "@tonaljs/roman-numeral"; /** * Given a tonic and a chord list expressed with roman numeral notation @@ -35,3 +35,5 @@ export function toRomanNumerals( return roman.name + chordType; }); } + +export default { fromRomanNumerals, toRomanNumerals }; diff --git a/packages/progression/progression.test.ts b/packages/progression/progression.test.ts index 54c093fc..0248b435 100644 --- a/packages/progression/progression.test.ts +++ b/packages/progression/progression.test.ts @@ -1,10 +1,11 @@ -import { fromRomanNumerals, toRomanNumerals } from "./index"; +import Progression from "./index"; const $ = (str: string) => str.split(" "); describe("@tonaljs/progression", () => { test("concrete", () => { - const inC = (chords: string[]) => fromRomanNumerals("C", chords); + const inC = (chords: string[]) => + Progression.fromRomanNumerals("C", chords); expect(inC($("I IIm7 V7"))).toEqual($("C Dm7 G7")); expect(inC($("Imaj7 2 IIIm7"))).toEqual(["Cmaj7", "", "Em7"]); expect(inC($("I II III IV V VI VII"))).toEqual($("C D E F G A B")); @@ -17,7 +18,7 @@ describe("@tonaljs/progression", () => { }); test("abstract", () => { - const roman = toRomanNumerals("C", ["Cmaj7", "Dm7", "G7"]); + const roman = Progression.toRomanNumerals("C", ["Cmaj7", "Dm7", "G7"]); expect(roman).toEqual(["Imaj7", "IIm7", "V7"]); }); }); diff --git a/packages/range/README.md b/packages/range/README.md index 414c401d..4d626557 100644 --- a/packages/range/README.md +++ b/packages/range/README.md @@ -1,20 +1,42 @@ # @tonaljs/range ![tonal](https://img.shields.io/badge/@tonaljs-range-yellow.svg?style=flat-square) [![npm version](https://img.shields.io/npm/v/@tonaljs/range.svg?style=flat-square)](https://www.npmjs.com/package/@tonaljs/range) -`@tonaljs/range` is a collection of functions to create (musical) note ranges +`@tonaljs/range` is a collection of functions to create note ranges + +## Usage + +ES6: + +```js +import { Range } from "@tonaljs/tonal"; +``` + +nodejs: + +```js +const { Range } = require("@tonaljs/tonal"); +``` + +Single module: + +```js +import Range from "@tonaljs/range"; +``` ## API -### `numeric(notes: Array) => number[]` +### `Range.numeric(notes: Array) => number[]` + +Create a numeric (midi) range. You supply a list of notes or numbers (peaks and valleys) and they will be connected to create complex ranges. -Create a numeric range. You supply a list of notes or numbers (peaks and valleys) and they will be conected to create complex ranges. Notes can be note names or midi numbers, ranges can be ascending or descending. +Arguments can be note names or midi numbers, ranges can be ascending or descending. ```js -numeric(["C5", "C4"]); // => [ 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60 ] -numeric([10, 5]); // => [ 10, 9, 8, 7, 6, 5 ] -numeric(["C4", "E4", "Bb3"]); // => [60, 61, 62, 63, 64, 63, 62, 61, 60, 59, 58] +Range.numeric(["C5", "C4"]); // => [ 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60 ] +Range.numeric([10, 5]); // => [ 10, 9, 8, 7, 6, 5 ] +Range.numeric(["C4", "E4", "Bb3"]); // => [60, 61, 62, 63, 64, 63, 62, 61, 60, 59, 58] ``` -### `range(notes: Array, options) => string[]` +### `Range.chromatic(notes: Array, options) => string[]` Create a range of chromatic notes. The same as `numeric` but the result will be note names instead of midi numbers. @@ -24,8 +46,8 @@ The optional `options` object defines how note names are built: - pitchClass: if `true` octaves are omited. Default is `false` ```js -chromatic(["C2", "E2", "D2"]); +Range.chromatic(["C2", "E2", "D2"]); // => ["C2", "Db2", "D2", "Eb2", "E2", "Eb2", "D2"] -chromatic(["C2", "C3"], { sharps: true }); +Range.chromatic(["C2", "C3"], { sharps: true }); // => [ "C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2", "C3" ] ``` diff --git a/packages/range/index.ts b/packages/range/index.ts index 3bed8c14..887df387 100644 --- a/packages/range/index.ts +++ b/packages/range/index.ts @@ -1,12 +1,12 @@ -import { compact, range } from "@tonaljs/array"; +import { compact, range } from "@tonaljs/collection"; import { midiToNoteName, toMidi, ToNoteNameOptions } from "@tonaljs/midi"; /** * Create a numeric range. You supply a list of notes or numbers and it will - * be conected to create complex ranges. + * be connected to create complex ranges. * * @param {Array} array - the list of notes or numbers used - * @return {Array} an array of numbers or empty array if not vald parameters + * @return {Array} an array of numbers or empty array if not valid parameters * * @example * numeric(["C5", "C4"]) // => [ 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60 ] @@ -49,3 +49,5 @@ export function chromatic( ): string[] { return numeric(notes).map(midi => midiToNoteName(midi, options)); } + +export default { numeric, chromatic }; diff --git a/packages/range/package.json b/packages/range/package.json index f427e553..9e69fb04 100644 --- a/packages/range/package.json +++ b/packages/range/package.json @@ -15,7 +15,7 @@ ], "types": "dist/range/index.d.ts", "dependencies": { - "@tonaljs/array": "^3.2.4", + "@tonaljs/collection": "^3.2.4", "@tonaljs/midi": "^3.3.2" }, "author": "danigb@gmail.com", diff --git a/packages/range/range.test.ts b/packages/range/range.test.ts index 33800886..f2698f0a 100644 --- a/packages/range/range.test.ts +++ b/packages/range/range.test.ts @@ -1,4 +1,5 @@ -import { chromatic, numeric } from "./index"; +import Range from "./index"; +const { numeric, chromatic } = Range; const $ = (str: string) => str.split(" "); diff --git a/packages/roman-numeral/README.md b/packages/roman-numeral/README.md index 8f6d3225..cfc3179e 100644 --- a/packages/roman-numeral/README.md +++ b/packages/roman-numeral/README.md @@ -4,16 +4,30 @@ A roman numeral symbol is a string like `"bVIImaj7"` that can be used to represent chords in an abstract tonallity. +## Usage + +ES6: + +```js +import { RomanNumeral } from "@tonaljs/tonal"; +``` + +node: + +```js +const { RomanNumeral } = require("@tonaljs/tonal"); +``` + ## API -### `roman-numeral(src: string | Pitch): => RomanNumeral` +### `RomanNumeral.get(src: string | Pitch): => RomanNumeral` Get the properties of a roman numeral: Example: ```js -romanNumeral("bVIIMaj7"); +RomanNumeral.get("bVIIMaj7"); // => // { // empty: false, @@ -35,10 +49,9 @@ romanNumeral("bVIIMaj7"); `romanNumeral` function accepts a `Pitch` as argument: ```js -import { interval } from "@tonaljs/note"; -import { roman-numeral } from "@tonaljs/roman-numeral"; +import { Interval, RomanNumeral } from "@tonaljs/tonal"; -romanNumeral(interval("3m")).name; // => "bIII" +RomanNumeral.get(Interval.get("3m")).name; // => "bIII" ``` ## Want more? diff --git a/packages/roman-numeral/index.ts b/packages/roman-numeral/index.ts index 07c26d96..3e135558 100644 --- a/packages/roman-numeral/index.ts +++ b/packages/roman-numeral/index.ts @@ -1,6 +1,7 @@ import { accToAlt, altToAcc, + deprecate, interval, isNamed, isPitch, @@ -41,18 +42,24 @@ const cache: Record = {}; * @example * romanNumeral("VIIb5") // => { name: "VII", type: "b5", num: 7, major: true } */ -export function romanNumeral(src: any): RomanNumeral | NoRomanNumeral { +export function get(src: any): RomanNumeral | NoRomanNumeral { return typeof src === "string" ? cache[src] || (cache[src] = parse(src)) : typeof src === "number" - ? romanNumeral(NAMES[src] || "") + ? get(NAMES[src] || "") : isPitch(src) ? fromPitch(src) : isNamed(src) - ? romanNumeral(src.name) + ? get(src.name) : NoRomanNumeral; } +const romanNumeral = deprecate( + "RomanNumeral.romanNumeral", + "RomanNumeral.get", + get +); + /** * Get roman numeral names * @@ -68,7 +75,7 @@ export function names(major = true) { } function fromPitch(pitch: Pitch): RomanNumeral | NoRomanNumeral { - return romanNumeral(altToAcc(pitch.alt) + NAMES[pitch.step]); + return get(altToAcc(pitch.alt) + NAMES[pitch.step]); } const REGEX = /^(#{1,}|b{1,}|x{1,}|)(IV|I{1,3}|VI{0,2}|iv|i{1,3}|vi{0,2})([^IViv]*)$/; @@ -107,3 +114,10 @@ function parse(src: string): RomanNumeral | NoRomanNumeral { dir }; } + +export default { + names, + get, + // deprecated + romanNumeral +}; diff --git a/packages/roman-numeral/roman-numeral.test.ts b/packages/roman-numeral/roman-numeral.test.ts index 703a28ae..ab043749 100644 --- a/packages/roman-numeral/roman-numeral.test.ts +++ b/packages/roman-numeral/roman-numeral.test.ts @@ -1,17 +1,33 @@ import { interval } from "@tonaljs/core"; -import { names, romanNumeral, RomanNumeral } from "./index"; +import RomanNumeral from "./index"; const $ = (str: string) => str.split(" "); describe("tonal-roman-numeral", () => { test("names", () => { - expect(names()).toEqual(["I", "II", "III", "IV", "V", "VI", "VII"]); - expect(names(false)).toEqual(["i", "ii", "iii", "iv", "v", "vi", "vii"]); + expect(RomanNumeral.names()).toEqual([ + "I", + "II", + "III", + "IV", + "V", + "VI", + "VII" + ]); + expect(RomanNumeral.names(false)).toEqual([ + "i", + "ii", + "iii", + "iv", + "v", + "vi", + "vii" + ]); }); describe("romanNumeral", () => { test("properties", () => { - expect(romanNumeral("#VIIb5")).toEqual({ + expect(RomanNumeral.get("#VIIb5")).toEqual({ empty: false, name: "#VIIb5", roman: "VII", @@ -27,42 +43,44 @@ describe("tonal-roman-numeral", () => { }); test("RomanNumeral is compatible with Pitch", () => { const naturals = $("1P 2M 3M 4P 5P 6M 7M").map(interval); - expect(naturals.map(romanNumeral).map(n => n.name)).toEqual( + expect(naturals.map(RomanNumeral.get).map(n => n.name)).toEqual( $("I II III IV V VI VII") ); const flats = $("1d 2m 3m 4d 5d 6m 7m").map(interval); - expect(flats.map(romanNumeral).map(n => n.name)).toEqual( + expect(flats.map(RomanNumeral.get).map(n => n.name)).toEqual( $("bI bII bIII bIV bV bVI bVII") ); const sharps = $("1A 2A 3A 4A 5A 6A 7A").map(interval); - expect(sharps.map(romanNumeral).map(n => n.name)).toEqual( + expect(sharps.map(RomanNumeral.get).map(n => n.name)).toEqual( $("#I #II #III #IV #V #VI #VII") ); }); test("Can convert to intervals", () => { - expect(interval(romanNumeral("I")).name).toEqual("1P"); - expect(interval(romanNumeral("bIIImaj4")).name).toEqual("3m"); - expect(interval(romanNumeral("#IV7")).name).toEqual("4A"); + expect(interval(RomanNumeral.get("I")).name).toEqual("1P"); + expect(interval(RomanNumeral.get("bIIImaj4")).name).toEqual("3m"); + expect(interval(RomanNumeral.get("#IV7")).name).toEqual("4A"); }); test("step", () => { - const decimal = (x: string) => romanNumeral(x).step; - expect(names().map(decimal)).toEqual([0, 1, 2, 3, 4, 5, 6]); + const decimal = (x: string) => RomanNumeral.get(x).step; + expect(RomanNumeral.names().map(decimal)).toEqual([0, 1, 2, 3, 4, 5, 6]); }); test("invalid", () => { - expect(romanNumeral("nothing").name).toEqual(""); - expect(romanNumeral("iI").name).toEqual(""); + expect(RomanNumeral.get("nothing").name).toEqual(""); + expect(RomanNumeral.get("iI").name).toEqual(""); }); it("roman", () => { - expect(romanNumeral("IIIMaj7").roman).toEqual("III"); - expect(names().map(x => romanNumeral(x).name)).toEqual(names()); + expect(RomanNumeral.get("IIIMaj7").roman).toEqual("III"); + expect(RomanNumeral.names().map(x => RomanNumeral.get(x).name)).toEqual( + RomanNumeral.names() + ); }); }); it("create from degrees", () => { - expect([1, 2, 3, 4, 5, 6, 7].map(i => romanNumeral(i - 1).name)).toEqual( - names() - ); + expect( + [1, 2, 3, 4, 5, 6, 7].map(i => RomanNumeral.get(i - 1).name) + ).toEqual(RomanNumeral.names()); }); }); diff --git a/packages/scale-dictionary/README.md b/packages/scale-dictionary/README.md index 45db22f1..f024767b 100644 --- a/packages/scale-dictionary/README.md +++ b/packages/scale-dictionary/README.md @@ -1,75 +1 @@ -# @tonaljs/scale-dictionary [![npm version](https://img.shields.io/npm/v/@tonaljs/scale-dictionary.svg?style=flat-square)](https://www.npmjs.com/package/@tonaljs/scale-dictionary) - -[![tonal](https://img.shields.io/badge/@tonaljs-scale_dictionary-yellow.svg?style=flat-square)](https://www.npmjs.com/browse/keyword/tonal) - -`@tonaljs/scale-dictionary` is a dictionary of musical scales. - -## Usage - -```js -import { ScaleDictionary } from "@tonaljs/tonal"; -``` - -## API - -#### `get(name: string) => ScaleType` - -Given a scale type name, return a ScaleType object with the following properties: - -- name: the scale type name -- aliases: a list of alternative names -- quality: Major | Minor | Augmented | Diminished | Unknown -- num: the pcset number -- chroma: the pcset chroma -- length: the number of notes -- intervals: the interval list - -Example: - -```js -ScaleDictionary.get("major"); // => -// { -// name: "major", -// aliases: ["ionian"], -// num: 2773, -// chroma: "101011010101", -// length: 7 -// intervals: ["1P", "2M", "3M", "4P", "5P", "6M", "7M"], -// }); -``` - -#### `entries() => Scale[]` - -Return a list of all available scale types - -#### `add(intervals: string[], name?: string, aliases?: string[]) => ScaleType` - -Add a scale type to dictionary: - -```js -ScaleDictionary.add(["1P", "5P"], null, ["5"]); -``` - -## HOW TO - -#### How to get all names? - -```js -ScaleDictionary.entries().map(scaleType => scaleType.name); -``` - -#### How to get all pentatonics names? - -```js -ScaleDictionary.entries() - .filter(scaleType => scaleType.length === 5) - .map(scaleType => scaleType.name); -``` - -#### How do to add a scale to the dictionary? - -```js -ScaleDictionary.add(["1P", "5P"], "quinta", ["quinta justa", "diapente"]); -ScaleDictionary.scale("quinta"); // => { name: "quinta", intervals: ...} -ScaleDictionary.scale("quinta justa"); // => { name: "quinta", intervals: ... } -``` +# Deprecated. Use [@tonaljs/scale-type](https://github.com/tonaljs/tonal/tree/master/packages/scale-type) diff --git a/packages/scale-dictionary/index.ts b/packages/scale-dictionary/index.ts index 4bc1ce46..ecceccb6 100644 --- a/packages/scale-dictionary/index.ts +++ b/packages/scale-dictionary/index.ts @@ -1,106 +1 @@ -import { - EmptyPcset, - pcset, - Pcset, - PcsetChroma, - PcsetNum -} from "@tonaljs/pcset"; -import data from "./data"; - -/** - * Properties for a scale in the scale dictionary. It's a pitch class set - * properties with the following additional information: - * - name: the scale name - * - aliases: alternative list of names - * - intervals: an array of interval names - */ -export interface ScaleType extends Pcset { - readonly name: string; - readonly aliases: string[]; -} - -export const NoScaleType: ScaleType = { - ...EmptyPcset, - intervals: [], - aliases: [] -}; - -type ScaleTypeName = string | PcsetChroma | PcsetNum; - -let scales: ScaleType[] = []; -let index: Record = {}; - -/** - * Given a scale name or chroma, return the scale properties - * - * @param {string} type - scale name or pitch class set chroma - * @example - * import { get } from 'tonaljs/scale-dictionary' - * get('major') // => { name: 'major', ... } - */ -export function get(type: ScaleTypeName): ScaleType { - return index[type] || NoScaleType; -} - -/** - * Given a scale name or chroma, return the scale properties - * @deprecated - * @see Scale.get - */ -export function scaleType(type: ScaleTypeName): ScaleType { - // tslint:disable-next-line - console.warn( - "ScaleDictionary.scaleType is deprecated. Use ScaleDictionary.get instead" - ); - return get(type); -} - -/** - * Return a list of all scale types - */ -export function entries() { - return scales.slice(); -} - -/** - * Keys used to reference scale types - */ -export function keys() { - return Object.keys(index); -} - -/** - * Clear the dictionary - */ -export function clear() { - scales = []; - index = {}; -} - -/** - * Add a scale into dictionary - * @param intervals - * @param name - * @param aliases - */ -export function add( - intervals: string[], - name: string, - aliases: string[] = [] -): ScaleType { - const scale = { ...pcset(intervals), name, intervals, aliases }; - scales.push(scale); - index[scale.name] = scale; - index[scale.setNum] = scale; - index[scale.chroma] = scale; - scale.aliases.forEach(alias => addAlias(scale, alias)); - return scale; -} - -export function addAlias(scale: ScaleType, alias: string) { - index[alias] = scale; -} - -data.forEach(([ivls, name, ...aliases]: string[]) => - add(ivls.split(" "), name, aliases) -); +export * from "@tonaljs/scale-type"; diff --git a/packages/scale-dictionary/package.json b/packages/scale-dictionary/package.json index ba9b3005..0117f8b7 100644 --- a/packages/scale-dictionary/package.json +++ b/packages/scale-dictionary/package.json @@ -2,13 +2,7 @@ "name": "@tonaljs/scale-dictionary", "version": "3.4.3", "description": "A dictionary of musical scales", - "keywords": [ - "scale", - "dictionary", - "music", - "theory", - "@tonaljs/core" - ], + "keywords": [], "main": "dist/index.es5.js", "module": "dist/index.esnext.js", "files": [ @@ -16,8 +10,7 @@ ], "types": "dist/scale-dictionary/index.d.ts", "dependencies": { - "@tonaljs/interval": "^3.2.4", - "@tonaljs/pcset": "^3.2.4" + "@tonaljs/scale-type": "^3.4.3" }, "author": "danigb@gmail.com", "license": "MIT", diff --git a/packages/scale-type/LICENSE b/packages/scale-type/LICENSE new file mode 100644 index 00000000..77ac35ab --- /dev/null +++ b/packages/scale-type/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 danigb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/packages/scale-type/README.md b/packages/scale-type/README.md new file mode 100644 index 00000000..95e4ccdb --- /dev/null +++ b/packages/scale-type/README.md @@ -0,0 +1,81 @@ +# @tonaljs/scale-type [![npm version](https://img.shields.io/npm/v/@tonaljs/scale-type.svg?style=flat-square)](https://www.npmjs.com/package/@tonaljs/scale-type) + +[![tonal](https://img.shields.io/badge/@tonaljs-scale_dictionary-yellow.svg?style=flat-square)](https://www.npmjs.com/browse/keyword/tonal) + +`@tonaljs/scale-type` is a dictionary of musical scales. + +## Usage + +ES6: + +```js +import { ScaleType } from "@tonaljs/tonal"; +``` + +nodejs: + +```js +const { ScaleType } = require("@tonaljs/tonal"); +``` + +## API + +#### `get(name: string) => ScaleType` + +Given a scale type name, return a ScaleType object with the following properties: + +- name: the scale type name +- aliases: a list of alternative names +- quality: Major | Minor | Augmented | Diminished | Unknown +- num: the pcset number +- chroma: the pcset chroma +- length: the number of notes +- intervals: the interval list + +Example: + +```js +ScaleType.get("major"); // => +// { +// name: "major", +// aliases: ["ionian"], +// num: 2773, +// chroma: "101011010101", +// length: 7 +// intervals: ["1P", "2M", "3M", "4P", "5P", "6M", "7M"], +// }); +``` + +#### `names() => string[]` + +Return a list of all scale names + +#### `all() => object[]` + +Return a list of all scale types + +#### `add(intervals: string[], name?: string, aliases?: string[]) => ScaleType` + +Add a scale type to dictionary: + +```js +ScaleType.add(["1P", "5P"], null, ["5"]); +``` + +## HOW TO + +#### How to get all pentatonics names? + +```js +ScaleType.all() + .filter(scaleType => scaleType.length === 5) + .map(scaleType => scaleType.name); +``` + +#### How do to add a scale to the dictionary? + +```js +ScaleType.add(["1P", "5P"], "quinta", ["quinta justa", "diapente"]); +ScaleType.scale("quinta"); // => { name: "quinta", intervals: ...} +ScaleType.scale("quinta justa"); // => { name: "quinta", intervals: ... } +``` diff --git a/packages/scale-dictionary/data.ts b/packages/scale-type/data.ts similarity index 100% rename from packages/scale-dictionary/data.ts rename to packages/scale-type/data.ts diff --git a/packages/scale-type/index.ts b/packages/scale-type/index.ts new file mode 100644 index 00000000..f145abd3 --- /dev/null +++ b/packages/scale-type/index.ts @@ -0,0 +1,123 @@ +import { deprecate } from "@tonaljs/core"; +import { + EmptyPcset, + get as pcset, + Pcset, + PcsetChroma, + PcsetNum +} from "@tonaljs/pcset"; +import data from "./data"; + +/** + * Properties for a scale in the scale dictionary. It's a pitch class set + * properties with the following additional information: + * - name: the scale name + * - aliases: alternative list of names + * - intervals: an array of interval names + */ +export interface ScaleType extends Pcset { + readonly name: string; + readonly aliases: string[]; +} + +export const NoScaleType: ScaleType = { + ...EmptyPcset, + intervals: [], + aliases: [] +}; + +type ScaleTypeName = string | PcsetChroma | PcsetNum; + +let dictionary: ScaleType[] = []; +let index: Record = {}; + +export function names() { + return dictionary.map(scale => scale.name); +} + +/** + * Given a scale name or chroma, return the scale properties + * + * @param {string} type - scale name or pitch class set chroma + * @example + * import { get } from 'tonaljs/scale-type' + * get('major') // => { name: 'major', ... } + */ +export function get(type: ScaleTypeName): ScaleType { + return index[type] || NoScaleType; +} + +export const scaleType = deprecate( + "ScaleDictionary.scaleType", + "ScaleType.get", + get +); + +/** + * Return a list of all scale types + */ +export function all() { + return dictionary.slice(); +} + +export const entries = deprecate( + "ScaleDictionary.entries", + "ScaleType.all", + all +); + +/** + * Keys used to reference scale types + */ +export function keys() { + return Object.keys(index); +} + +/** + * Clear the dictionary + */ +export function removeAll() { + dictionary = []; + index = {}; +} + +/** + * Add a scale into dictionary + * @param intervals + * @param name + * @param aliases + */ +export function add( + intervals: string[], + name: string, + aliases: string[] = [] +): ScaleType { + const scale = { ...pcset(intervals), name, intervals, aliases }; + dictionary.push(scale); + index[scale.name] = scale; + index[scale.setNum] = scale; + index[scale.chroma] = scale; + scale.aliases.forEach(alias => addAlias(scale, alias)); + return scale; +} + +export function addAlias(scale: ScaleType, alias: string) { + index[alias] = scale; +} + +data.forEach(([ivls, name, ...aliases]: string[]) => + add(ivls.split(" "), name, aliases) +); + +export default { + names, + get, + all, + add, + removeAll, + keys, + + // deprecated + entries, + scaleType +}; diff --git a/packages/scale-type/package.json b/packages/scale-type/package.json new file mode 100644 index 00000000..ee78255c --- /dev/null +++ b/packages/scale-type/package.json @@ -0,0 +1,27 @@ +{ + "name": "@tonaljs/scale-type", + "version": "3.4.3", + "description": "A dictionary of musical scales", + "keywords": [ + "scale", + "dictionary", + "music", + "theory", + "@tonaljs/core" + ], + "main": "dist/index.es5.js", + "module": "dist/index.esnext.js", + "files": [ + "dist" + ], + "types": "dist/scale-type/index.d.ts", + "dependencies": { + "@tonaljs/core": "^3.2.4", + "@tonaljs/pcset": "^3.2.4" + }, + "author": "danigb@gmail.com", + "license": "MIT", + "publishConfig": { + "access": "public" + } +} diff --git a/packages/scale-dictionary/scale-dictionary.test.ts b/packages/scale-type/scale-type.test.ts similarity index 54% rename from packages/scale-dictionary/scale-dictionary.test.ts rename to packages/scale-type/scale-type.test.ts index 477c2fdf..31d7d018 100644 --- a/packages/scale-dictionary/scale-dictionary.test.ts +++ b/packages/scale-type/scale-type.test.ts @@ -1,15 +1,15 @@ -import { modes } from "../pcset"; -import { add, clear, entries, get, keys } from "./index"; +import { modes } from "@tonaljs/pcset"; +import ScaleType from "./index"; describe("gets dictionary", () => { test("list names", () => { - expect(entries()).toHaveLength(88); + expect(ScaleType.all()).toHaveLength(88); // sorted - expect(entries()[0].name).toEqual("major pentatonic"); + expect(ScaleType.all()[0].name).toEqual("major pentatonic"); }); test("get ", () => { - expect(get("major")).toEqual({ + expect(ScaleType.get("major")).toEqual({ empty: false, setNum: 2773, name: "major", @@ -21,7 +21,7 @@ describe("gets dictionary", () => { }); test("not valid get type", () => { - expect(get("unknown")).toEqual({ + expect(ScaleType.get("unknown")).toEqual({ empty: true, name: "", setNum: 0, @@ -33,18 +33,18 @@ describe("gets dictionary", () => { }); test("add a chord type", () => { - add(["1P", "5P"], "quinta"); - expect(get("quinta")).toMatchObject({ + ScaleType.add(["1P", "5P"], "quinta"); + expect(ScaleType.get("quinta")).toMatchObject({ chroma: "100000010000" }); - add(["1P", "5P"], "quinta", ["q", "Q"]); - expect(get("q")).toEqual(get("quinta")); - expect(get("Q")).toEqual(get("quinta")); + ScaleType.add(["1P", "5P"], "quinta", ["q", "Q"]); + expect(ScaleType.get("q")).toEqual(ScaleType.get("quinta")); + expect(ScaleType.get("Q")).toEqual(ScaleType.get("quinta")); }); test("major modes", () => { - const chromas = modes(get("major").intervals, true); - const names = chromas.map(chroma => get(chroma).name); + const chromas = modes(ScaleType.get("major").intervals, true); + const names = chromas.map(chroma => ScaleType.get(chroma).name); expect(names).toEqual([ "major", "dorian", @@ -56,8 +56,8 @@ describe("gets dictionary", () => { ]); }); test("harmonic minor modes", () => { - const chromas = modes(get("harmonic minor").intervals, true); - const names = chromas.map(chroma => get(chroma).name); + const chromas = modes(ScaleType.get("harmonic minor").intervals, true); + const names = chromas.map(chroma => ScaleType.get(chroma).name); expect(names).toEqual([ "harmonic minor", "locrian 6", @@ -69,8 +69,8 @@ describe("gets dictionary", () => { ]); }); test("melodic minor modes", () => { - const chromas = modes(get("melodic minor").intervals, true); - const names = chromas.map(chroma => get(chroma).name); + const chromas = modes(ScaleType.get("melodic minor").intervals, true); + const names = chromas.map(chroma => ScaleType.get(chroma).name); expect(names).toEqual([ "melodic minor", "dorian b2", @@ -83,8 +83,8 @@ describe("gets dictionary", () => { }); test("clear dictionary", () => { - clear(); - expect(entries()).toEqual([]); - expect(keys()).toEqual([]); + ScaleType.removeAll(); + expect(ScaleType.all()).toEqual([]); + expect(ScaleType.keys()).toEqual([]); }); }); diff --git a/packages/scale/README.md b/packages/scale/README.md index a26786a2..2013b225 100644 --- a/packages/scale/README.md +++ b/packages/scale/README.md @@ -4,16 +4,40 @@ `@tonaljs/scale` is a collection of functions to create and manipulate musical scales +## Usage + +ES6: + +```js +import { Scale } from "@tonaljs/tonal"; +``` + +nodejs: + +```js +const { Scale } = require("@tonaljs/tonal"); +``` + +Single module: + +```js +import Scale from "@tonaljs/scale"; +``` + ## API -### `scale(name: string) => Scale` +### `Scale.names()` -Get a scale from a scale name. Unlike `scaleType`, `scale` accepts tonics in the scale name and returns the scale type with two more properties: `tonic` and `notes`: +List all known scale names. Same as `ScaleType.names()` -See [scale-dictionary](../scale-dictionary) for more details. +See [scale-type](/package/scale-type) + +### `Scale.get(name: string) => Scale` + +Get a scale from a scale name. `Scale.get` accepts tonics in the scale name and returns a [scale type](/packages/scale-type) with two more properties: `tonic` and `notes`: ```js -scale("c5 pentatonic"); +Scale.get("c5 pentatonic"); // => // { // empty: false, @@ -29,25 +53,25 @@ scale("c5 pentatonic"); // } ``` -### `chords(scale: string) => string[]` +### `Scale.chords(scale: string) => string[]` -Get all chords that fits a given scale +Get all chords that fits a given scale: ```js Scale.chords("pentatonic"); // => ["5", "64", "M", "M6", "Madd9", "Msus2"] ``` -### `extended(scale: string) => string[]` +### `Scale.extended(scale: string) => string[]` -Get all scales names that are a superset of the given one (has the same notes and at least one more) +Get all scales names that has the same notes and at least one more: ```js Scale.extended("major"); // => ["bebop", "bebop dominant", "bebop major", "chromatic", "ichikosucho"] ``` -### `reduced(scale: string) => string[]` +### `Scale.reduced(scale: string) => string[]` Find all scales names that are a subset of the given one (less notes but all from the given scale) @@ -55,3 +79,26 @@ Find all scales names that are a subset of the given one (less notes but all fro Scale.reduced("major"); // => ["ionian pentatonic", "major pentatonic", "ritusen"] ``` + +### `Scale.scaleNotes(notes: string[]) => string[]` + +Given an array of notes, return the scale: a pitch class set starting from the first note + +```js +Scale.scaleNotes(["C4", "c3", "C5", "C4", "c4"]); // => ["C"] +Scale.scaleNotes(["D4", "c#5", "A5", "F#6"]); // => ["D", "F#", "A", "C#"] +``` + +### `Scale.modes(name: string) => string[][]` + +Find mode names (if any) of a given scale: + +```js +Scale.modeNames("C pentatonic"); // => [ +// ["C", "major pentatonic"], +// ["D", "egyptian"], +// ["E", "malkos raga"], +// ["G", "ritusen"], +// ["A", "minor pentatonic"] +// ] +``` diff --git a/packages/scale/index.ts b/packages/scale/index.ts index 8527f196..92d2bb60 100644 --- a/packages/scale/index.ts +++ b/packages/scale/index.ts @@ -3,15 +3,17 @@ * - https://www.researchgate.net/publication/327567188_An_Algorithm_for_Spelling_the_Pitches_of_Any_Musical_Scale * @module scale */ -import { rotate, sortedUniqNoteNames } from "@tonaljs/array"; -import { entries as chordTypes } from "@tonaljs/chord-dictionary"; -import { note, NoteName, transpose } from "@tonaljs/core"; +import { all as chordTypes } from "@tonaljs/chord-type"; +import { rotate } from "@tonaljs/collection"; +import { deprecate, note, NoteName, transpose } from "@tonaljs/core"; +import { sortedUniqNames } from "@tonaljs/note"; import { isSubsetOf, isSupersetOf, modes } from "@tonaljs/pcset"; import { - entries as scaleTypes, + all as scaleTypes, get as getScaleType, + names as scaleTypeNames, ScaleType -} from "@tonaljs/scale-dictionary"; +} from "@tonaljs/scale-type"; type ScaleName = string; type ScaleNameTokens = [string, string]; // [TONIC, SCALE TYPE] @@ -66,10 +68,16 @@ export function tokenize(name: ScaleName): ScaleNameTokens { return [tonic.name, type.length ? type : ""]; } +/** + * Get all scale names + * @function + */ +export const names = scaleTypeNames; + /** * Get a Scale from a scale name. */ -export function scale(src: ScaleName | ScaleNameTokens): Scale { +export function get(src: ScaleName | ScaleNameTokens): Scale { const tokens = Array.isArray(src) ? src : tokenize(src); const tonic = note(tokens[0]).name; const st = getScaleType(tokens[1]); @@ -87,6 +95,8 @@ export function scale(src: ScaleName | ScaleNameTokens): Scale { return { ...st, name, type, tonic, notes }; } +export const scale = deprecate("Scale.scale", "Scale.get", get); + /** * Get all chords that fits a given scale * @@ -98,7 +108,7 @@ export function scale(src: ScaleName | ScaleNameTokens): Scale { * scaleChords("pentatonic") // => ["5", "64", "M", "M6", "Madd9", "Msus2"] */ export function scaleChords(name: string): string[] { - const s = scale(name); + const s = get(name); const inScale = isSubsetOf(s.chroma); return chordTypes() .filter(chord => inScale(chord.chroma)) @@ -115,7 +125,7 @@ export function scaleChords(name: string): string[] { * extended("major") // => ["bebop", "bebop dominant", "bebop major", "chromatic", "ichikosucho"] */ export function extended(name: string): string[] { - const s = scale(name); + const s = get(name); const isSuperset = isSupersetOf(s.chroma); return scaleTypes() .filter(scale => isSuperset(scale.chroma)) @@ -134,7 +144,7 @@ export function extended(name: string): string[] { * reduced("major") // => ["ionian pentatonic", "major pentatonic", "ritusen"] */ export function reduced(name: string): string[] { - const isSubset = isSubsetOf(scale(name).chroma); + const isSubset = isSubsetOf(get(name).chroma); return scaleTypes() .filter(scale => isSubset(scale.chroma)) .map(scale => scale.name); @@ -154,7 +164,7 @@ export function reduced(name: string): string[] { export function scaleNotes(notes: NoteName[]) { const pcset: string[] = notes.map(n => note(n).pc).filter(x => x); const tonic = pcset[0]; - const scale = sortedUniqNoteNames(pcset); + const scale = sortedUniqNames(pcset); return rotate(scale.indexOf(tonic), scale); } @@ -174,7 +184,7 @@ type ScaleMode = [string, string]; * ] */ export function modeNames(name: string): ScaleMode[] { - const s = scale(name); + const s = get(name); if (s.empty) { return []; } @@ -183,9 +193,22 @@ export function modeNames(name: string): ScaleMode[] { return modes(s.chroma) .map( (chroma: string, i: number): ScaleMode => { - const modeName = scale(chroma).name; + const modeName = get(chroma).name; return modeName ? [tonics[i], modeName] : ["", ""]; } ) .filter(x => x[0]); } + +export default { + get, + names, + extended, + modeNames, + reduced, + scaleChords, + scaleNotes, + tokenize, + // deprecated + scale +}; diff --git a/packages/scale/package.json b/packages/scale/package.json index 11d2b3ce..b8e25034 100644 --- a/packages/scale/package.json +++ b/packages/scale/package.json @@ -16,11 +16,12 @@ ], "types": "dist/scale/index.d.ts", "dependencies": { - "@tonaljs/array": "^3.2.4", - "@tonaljs/chord-dictionary": "^3.4.1", + "@tonaljs/collection": "^3.2.4", + "@tonaljs/chord-type": "^3.4.1", "@tonaljs/core": "^3.3.0", + "@tonaljs/note": "^3.2.0", "@tonaljs/pcset": "^3.2.4", - "@tonaljs/scale-dictionary": "^3.4.3" + "@tonaljs/scale-type": "^3.4.3" }, "author": "danigb@gmail.com", "license": "MIT", diff --git a/packages/scale/scale.test.ts b/packages/scale/scale.test.ts index e9237e6e..517d4284 100644 --- a/packages/scale/scale.test.ts +++ b/packages/scale/scale.test.ts @@ -1,18 +1,10 @@ -import { - extended, - modeNames, - reduced, - scale, - scaleChords, - scaleNotes, - tokenize -} from "./index"; +import Scale from "./index"; const $ = (s: string) => s.split(" "); describe("@tonaljs/scale", () => { test("scale", () => { - expect(scale("major")).toEqual({ + expect(Scale.get("major")).toEqual({ empty: false, tonic: "", notes: [], @@ -24,7 +16,7 @@ describe("@tonaljs/scale", () => { chroma: "101011010101", normalized: "101010110101" }); - expect(scale("c5 pentatonic")).toEqual({ + expect(Scale.get("c5 pentatonic")).toEqual({ empty: false, name: "C5 major pentatonic", type: "major pentatonic", @@ -36,91 +28,98 @@ describe("@tonaljs/scale", () => { chroma: "101010010100", normalized: "100101001010" }); - expect(scale("C4 major")).toEqual(scale(["C4", "major"])); + expect(Scale.get("C4 major")).toEqual(Scale.get(["C4", "major"])); }); test("tokenize", () => { - expect(tokenize("c major")).toEqual(["C", "major"]); - expect(tokenize("cb3 major")).toEqual(["Cb3", "major"]); - expect(tokenize("melodic minor")).toEqual(["", "melodic minor"]); - expect(tokenize("dorian")).toEqual(["", "dorian"]); - expect(tokenize("c")).toEqual(["C", ""]); - expect(tokenize("")).toEqual(["", ""]); + expect(Scale.tokenize("c major")).toEqual(["C", "major"]); + expect(Scale.tokenize("cb3 major")).toEqual(["Cb3", "major"]); + expect(Scale.tokenize("melodic minor")).toEqual(["", "melodic minor"]); + expect(Scale.tokenize("dorian")).toEqual(["", "dorian"]); + expect(Scale.tokenize("c")).toEqual(["C", ""]); + expect(Scale.tokenize("")).toEqual(["", ""]); }); test("isKnown", () => { - expect(scale("major").empty).toBe(false); - expect(scale("Db major").empty).toBe(false); - expect(scale("hello").empty).toBe(true); - expect(scale("").empty).toBe(true); - expect(scale("Maj7").empty).toBe(true); + expect(Scale.get("major").empty).toBe(false); + expect(Scale.get("Db major").empty).toBe(false); + expect(Scale.get("hello").empty).toBe(true); + expect(Scale.get("").empty).toBe(true); + expect(Scale.get("Maj7").empty).toBe(true); }); test("intervals", () => { - expect(scale("major").intervals).toEqual($("1P 2M 3M 4P 5P 6M 7M")); - expect(scale("C major").intervals).toEqual($("1P 2M 3M 4P 5P 6M 7M")); - expect(scale("blah").intervals).toEqual([]); + expect(Scale.get("major").intervals).toEqual($("1P 2M 3M 4P 5P 6M 7M")); + expect(Scale.get("C major").intervals).toEqual($("1P 2M 3M 4P 5P 6M 7M")); + expect(Scale.get("blah").intervals).toEqual([]); }); test("notes", () => { - expect(scale("C major").notes).toEqual($("C D E F G A B")); - expect(scale("C lydian #9").notes).toEqual($("C D# E F# G A B")); - expect(scale(["C", "major"]).notes).toEqual($("C D E F G A B")); - expect(scale(["C4", "major"]).notes).toEqual($("C4 D4 E4 F4 G4 A4 B4")); - expect(scale(["eb", "bebop"]).notes).toEqual($("Eb F G Ab Bb C Db D")); - expect(scale(["C", "no-scale"]).notes).toEqual([]); - expect(scale(["no-note", "major"]).notes).toEqual([]); + expect(Scale.get("C major").notes).toEqual($("C D E F G A B")); + expect(Scale.get("C lydian #9").notes).toEqual($("C D# E F# G A B")); + expect(Scale.get(["C", "major"]).notes).toEqual($("C D E F G A B")); + expect(Scale.get(["C4", "major"]).notes).toEqual($("C4 D4 E4 F4 G4 A4 B4")); + expect(Scale.get(["eb", "bebop"]).notes).toEqual($("Eb F G Ab Bb C Db D")); + expect(Scale.get(["C", "no-scale"]).notes).toEqual([]); + expect(Scale.get(["no-note", "major"]).notes).toEqual([]); }); test("chords: find all chords that fits into this scale", () => { - expect(scaleChords("pentatonic")).toEqual($("5 M 6 sus2 Madd9")); - expect(scaleChords("none")).toEqual([]); + expect(Scale.scaleChords("pentatonic")).toEqual($("5 M 6 sus2 Madd9")); + expect(Scale.scaleChords("none")).toEqual([]); }); test("extended: find all scales that extends this one", () => { - expect(extended("major")).toEqual([ + expect(Scale.extended("major")).toEqual([ "bebop", "bebop major", "ichikosucho", "chromatic" ]); - expect(extended("none")).toEqual([]); + expect(Scale.extended("none")).toEqual([]); }); - test("reduced: all scales that are included in the given one", () => { - expect(reduced("major")).toEqual([ + test("Scale.reduced: all scales that are included in the given one", () => { + expect(Scale.reduced("major")).toEqual([ "major pentatonic", "ionian pentatonic", "ritusen" ]); - expect(reduced("D major")).toEqual(reduced("major")); - expect(reduced("none")).toEqual([]); + expect(Scale.reduced("D major")).toEqual(Scale.reduced("major")); + expect(Scale.reduced("none")).toEqual([]); }); - test("toScale", () => { - expect(scaleNotes($("C4 c3 C5 C4 c4"))).toEqual(["C"]); - expect(scaleNotes($("C4 f3 c#10 b5 d4 cb4"))).toEqual($("C C# D F B Cb")); - expect(scaleNotes($("D4 c#5 A5 F#6"))).toEqual(["D", "F#", "A", "C#"]); + test("scaleNotes", () => { + expect(Scale.scaleNotes($("C4 c3 C5 C4 c4"))).toEqual(["C"]); + expect(Scale.scaleNotes($("C4 f3 c#10 b5 d4 cb4"))).toEqual( + $("C C# D F B Cb") + ); + expect(Scale.scaleNotes($("D4 c#5 A5 F#6"))).toEqual([ + "D", + "F#", + "A", + "C#" + ]); }); test("mode names", () => { - expect(modeNames("pentatonic")).toEqual([ + expect(Scale.modeNames("pentatonic")).toEqual([ ["1P", "major pentatonic"], ["2M", "egyptian"], ["3M", "malkos raga"], ["5P", "ritusen"], ["6M", "minor pentatonic"] ]); - expect(modeNames("whole tone pentatonic")).toEqual([ + expect(Scale.modeNames("whole tone pentatonic")).toEqual([ ["1P", "whole tone pentatonic"] ]); - expect(modeNames("C pentatonic")).toEqual([ + expect(Scale.modeNames("C pentatonic")).toEqual([ ["C", "major pentatonic"], ["D", "egyptian"], ["E", "malkos raga"], ["G", "ritusen"], ["A", "minor pentatonic"] ]); - expect(modeNames("C whole tone pentatonic")).toEqual([ + expect(Scale.modeNames("C whole tone pentatonic")).toEqual([ ["C", "whole tone pentatonic"] ]); }); diff --git a/packages/tonal/browser/tonal.min.js b/packages/tonal/browser/tonal.min.js index ca5d975e..75b8aaec 100644 --- a/packages/tonal/browser/tonal.min.js +++ b/packages/tonal/browser/tonal.min.js @@ -1,2 +1,2 @@ -var Tonal=function(){"use strict";"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;function n(n){return"object"==typeof n&&"string"==typeof n.name}function t(n){return"object"==typeof n&&"number"==typeof n.step&&"number"==typeof n.alt}const e=[0,2,4,-1,1,3,5],o=e.map(n=>Math.floor(7*n/12));function m(n){const{step:t,alt:m,oct:r,dir:a=1}=n,i=e[t]+7*m;return void 0===r?[a*i]:[a*i,a*(r-o[t]-4*m)]}const r=[3,0,4,1,5,2,6];function a(n){const[t,e,m]=n,a=r[function(n){const t=(n+1)%7;return t<0?7+t:t}(t)],i=Math.floor((t+1)/7);return void 0===e?{step:a,alt:i,dir:m}:{step:a,alt:i,oct:e+4*i+o[a],dir:m}}const i={empty:!0,name:"",pc:"",acc:""},c={},s=(n,t)=>Array(t+1).join(n),P=n=>n<0?s("b",-n):s("#",n),M=n=>"b"===n[0]?-n.length:n.length;function u(e){return"string"==typeof e?c[e]||(c[e]=function(n){const t=d(n);if(""===t[0]||""!==t[3])return i;const e=t[0],o=t[1],r=t[2],a=(e.charCodeAt(0)+3)%7,c=M(o),s=r.length?+r:void 0,P=m({step:a,alt:c,oct:s}),u=e+o+r,l=e+o,p=(f[a]+c+120)%12,h=void 0===s?-100:s,b=f[a]+c+12*(h+1),y=b>=0&&b<=127?b:null,A=void 0===s?null:440*Math.pow(2,(b-69)/12);return{empty:!1,acc:o,alt:c,chroma:p,coord:P,freq:A,height:b,letter:e,midi:y,name:u,oct:s,pc:l,step:a}}(e)):t(e)?u(function(n){const{step:t,alt:e,oct:o}=n,m=(n=>"CDEFGAB".charAt(n))(t);if(!m)return"";const r=m+P(e);return o||0===o?r+o:r}(e)):n(e)?u(e.name):i}const l=/^([a-gA-G]?)(#{1,}|b{1,}|x{1,}|)(-?\d*)\s*(.*)$/;function d(n){const t=l.exec(n);return[t[1].toUpperCase(),t[2].replace(/x/g,"##"),t[3],t[4]]}function p(n){return u(a(n))}const f=[0,2,4,5,7,9,11];const h={empty:!0,name:"",acc:""},b=new RegExp("^([-+]?\\d+)(d{1,4}|m|M|P|A{1,4})|(AA|A|P|M|m|d|dd)([-+]?\\d+)$");function y(n){const t=b.exec(`${n}`);return null===t?["",""]:t[1]?[t[1],t[2]]:[t[4],t[3]]}const A={};function g(e){return"string"==typeof e?A[e]||(A[e]=function(n){const t=y(n);if(""===t[0])return h;const e=+t[0],o=t[1],r=(Math.abs(e)-1)%7,a="PMMPPMM"[r];if("M"===a&&"P"===o)return h;const i="M"===a?"majorable":"perfectable",c=""+e+o,s=e<0?-1:1,P=8===e||-8===e?e:s*(r+1),M=function(n,t){return"M"===t&&"majorable"===n||"P"===t&&"perfectable"===n?0:"m"===t&&"majorable"===n?-1:/^A+$/.test(t)?t.length:/^d+$/.test(t)?-1*("perfectable"===n?t.length:t.length+1):0}(i,o),u=Math.floor((Math.abs(e)-1)/7),l=s*(j[r]+M+12*u),d=(s*(j[r]+M)%12+12)%12,p=m({step:r,alt:M,oct:u,dir:s});return{empty:!1,name:c,num:e,q:o,step:r,alt:M,dir:s,type:i,simple:P,semitones:l,chroma:d,coord:p,oct:u}}(e)):t(e)?g(function(n){const{step:t,alt:e,oct:o=0,dir:m}=n;if(!m)return"";return(m<0?"-":"")+(t+1+7*o)+function(n,t){return 0===t?"majorable"===n?"M":"P":-1===t&&"majorable"===n?"m":t>0?_("A",t):_("d","perfectable"===n?t:t+1)}("M"==="PMMPPMM"[t]?"majorable":"perfectable",e)}(e)):n(e)?g(e.name):h}const j=[0,2,4,5,7,9,11];function I(n){const[t,e=0]=n;return g(a(7*t+12*e<0?[-t,-e,-1]:[t,e,1]))}const _=(n,t)=>Array(Math.abs(t)+1).join(n);function v(n,t){const e=u(n),o=g(t);if(e.empty||o.empty)return"";const m=e.coord,r=o.coord;return p(1===m.length?[m[0]+r[0]]:[m[0]+r[0],m[1]+r[1]]).name}function N(n,t){const e=u(n),o=u(t);if(e.empty||o.empty)return"";const m=e.coord,r=o.coord,a=r[0]-m[0];return I([a,2===m.length&&2===r.length?r[1]-m[1]:-Math.floor(7*a/12)]).name}var V=Object.freeze({__proto__:null,accToAlt:M,altToAcc:P,coordToInterval:I,coordToNote:p,decode:a,distance:N,encode:m,interval:g,isNamed:n,isPitch:t,note:u,tokenizeInterval:y,tokenizeNote:d,transpose:v});const D=(n,t)=>Array(t+1).join(n),x=/^(_{1,}|=|\^{1,}|)([abcdefgABCDEFG])([,']*)$/;function T(n){const t=x.exec(n);return t?[t[1],t[2],t[3]]:["","",""]}function z(n){const[t,e,o]=T(n);if(""===e)return"";let m=4;for(let n=0;n96?e.toUpperCase()+r+(m+1):e+r+m}function O(n){const t=u(n);if(t.empty||!t.oct)return"";const{letter:e,acc:o,oct:m}=t;return("b"===o[0]?o.replace(/b/g,"_"):o.replace(/#/g,"^"))+(m>4?e.toLowerCase():e)+(5===m?"":m>4?D("'",m-5):D(",",4-m))}var S=Object.freeze({__proto__:null,abcToScientificNotation:z,scientificToAbcNotation:O,tokenize:T,transpose:function(n,t){return O(v(z(n),t))}});function C(n,t){return n0===n||n)}function E(n){return n.map(n=>u(n)).filter(n=>!n.empty).sort((n,t)=>n.height-t.height).map(n=>n.name)}function q(n){return E(n).filter((n,t,e)=>0===t||n!==e[t-1])}var F=Object.freeze({__proto__:null,compact:w,permutations:function n(t){return 0===t.length?[[]]:n(t.slice(1)).reduce((n,e)=>n.concat(t.map((n,o)=>{const m=e.slice();return m.splice(o,0,t[0]),m})),[])},range:C,rotate:k,shuffle:function(n,t=Math.random){let e,o,m=n.length;for(;m;)e=Math.floor(t()*m--),o=n[m],n[m]=n[e],n[e]=o;return n},sortedNoteNames:E,sortedUniqNoteNames:q});const $={empty:!0,name:"",setNum:0,chroma:"000000000000",normalized:"000000000000",intervals:[]},U=n=>Number(n).toString(2),R=n=>parseInt(n,2),G=/^[01]{12}$/;function B(n){return G.test(n)}const K={[$.chroma]:$};function L(n){const t=B(n)?n:"number"==typeof(e=n)&&e>=0&&e<=4095?U(n):Array.isArray(n)?function(n){if(0===n.length)return $.chroma;let t;const e=[0,0,0,0,0,0,0,0,0,0,0,0];for(let o=0;on&&B(n.chroma))(n)?n.chroma:$.chroma;var e;return K[t]=K[t]||function(n){const t=R(n),e=function(n){const t=n.split("");return t.map((n,e)=>k(e,t).join(""))}(n).map(R).filter(n=>n>=2048).sort()[0],o=U(e),m=J(n);return{empty:!1,name:"",setNum:t,chroma:n,normalized:o,intervals:m}}(t)}const H="1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M".split(" ");function J(n){const t=[];for(let e=0;e<12;e++)"1"===n.charAt(e)&&t.push(H[e]);return t}let Q;function W(n,t=!0){const e=L(n).chroma.split("");return w(e.map((n,o)=>{const m=k(o,e);return t&&"0"===m[0]?null:m.join("")}))}function X(n){const t=L(n).setNum;return n=>{const e=L(n).setNum;return t&&t!==e&&(e&t)===e}}function Y(n){const t=L(n).setNum;return n=>{const e=L(n).setNum;return t&&t!==e&&(e|t)===e}}function Z(n){const t=L(n);return n=>{const e=u(n);return t&&!e.empty&&"1"===t.chroma.charAt(e.chroma)}}const nn=Z;var tn=Object.freeze({__proto__:null,EmptyPcset:$,chromaToIntervals:J,chromas:function(){return Q=Q||C(2048,4095).map(U),Q.slice()},filter:function(n){const t=Z(n);return n=>n.filter(t)},includes:nn,isEqual:function(n,t){return L(n).setNum===L(t).setNum},isNoteIncludedInSet:Z,isSubsetOf:X,isSupersetOf:Y,modes:W,pcset:L});const en={...$,name:"",quality:"Unknown",intervals:[],aliases:[]};let on=[],mn={};function rn(n){return mn[n]||en}function an(){return on.slice()}function cn(n,t,e){const o=function(n){const t=t=>-1!==n.indexOf(t);return t("5A")?"Augmented":t("3M")?"Major":t("5d")?"Diminished":t("3m")?"Minor":"Unknown"}(n),m={...L(n),name:e||"",quality:o,intervals:n,aliases:t};on.push(m),m.name&&(mn[m.name]=m),mn[m.setNum]=m,mn[m.chroma]=m,m.aliases.forEach(n=>sn(m,n))}function sn(n,t){mn[t]=n}[["1P 3M 5P","major","M "],["1P 3M 5P 7M","major seventh","maj7 Δ ma7 M7 Maj7"],["1P 3M 5P 7M 9M","major ninth","maj9 Δ9"],["1P 3M 5P 7M 9M 13M","major thirteenth","maj13 Maj13"],["1P 3M 5P 6M","sixth","6 add6 add13 M6"],["1P 3M 5P 6M 9M","sixth/ninth","6/9 69"],["1P 3M 5P 7M 11A","lydian","maj#4 Δ#4 Δ#11"],["1P 3M 6m 7M","major seventh b6","M7b6"],["1P 3m 5P","minor","m min -"],["1P 3m 5P 7m","minor seventh","m7 min7 mi7 -7"],["1P 3m 5P 7M","minor/major seventh","m/ma7 m/maj7 mM7 m/M7 -Δ7 mΔ"],["1P 3m 5P 6M","minor sixth","m6"],["1P 3m 5P 7m 9M","minor ninth","m9"],["1P 3m 5P 7m 9M 11P","minor eleventh","m11"],["1P 3m 5P 7m 9M 13M","minor thirteenth","m13"],["1P 3m 5d","diminished","dim ° o"],["1P 3m 5d 7d","diminished seventh","dim7 °7 o7"],["1P 3m 5d 7m","half-diminished","m7b5 ø"],["1P 3M 5P 7m","dominant seventh","7 dom"],["1P 3M 5P 7m 9M","dominant ninth","9"],["1P 3M 5P 7m 9M 13M","dominant thirteenth","13"],["1P 3M 5P 7m 11A","lydian dominant seventh","7#11 7#4"],["1P 3M 5P 7m 9m","dominant b9","7b9"],["1P 3M 5P 7m 9A","dominant #9","7#9"],["1P 3M 7m 9m","altered","alt7"],["1P 4P 5P","suspended 4th","sus4"],["1P 2M 5P","suspended 2nd","sus2"],["1P 4P 5P 7m","suspended 4th seventh","7sus4"],["1P 5P 7m 9M 11P","eleventh","11"],["1P 4P 5P 7m 9m","suspended 4th b9","b9sus phryg"],["1P 5P","fifth","5"],["1P 3M 5A","augmented","aug + +5"],["1P 3M 5A 7M","augmented seventh","maj7#5 maj7+5"],["1P 3M 5P 7M 9M 11A","major #11 (lydian)","maj9#11 Δ9#11"],["1P 2M 4P 5P","","sus24 sus4add9"],["1P 3M 13m","","Mb6"],["1P 3M 5A 7M 9M","","maj9#5 Maj9#5"],["1P 3M 5A 7m","","7#5 +7 7aug aug7"],["1P 3M 5A 7m 9A","","7#5#9 7alt 7#5#9_ 7#9b13_"],["1P 3M 5A 7m 9M","","9#5 9+"],["1P 3M 5A 7m 9M 11A","","9#5#11"],["1P 3M 5A 7m 9m","","7#5b9"],["1P 3M 5A 7m 9m 11A","","7#5b9#11"],["1P 3M 5A 9A","","+add#9"],["1P 3M 5A 9M","","M#5add9 +add9"],["1P 3M 5P 6M 11A","","M6#11 M6b5 6#11 6b5"],["1P 3M 5P 6M 7M 9M","","M7add13"],["1P 3M 5P 6M 9M 11A","","69#11"],["1P 3M 5P 6m 7m","","7b6"],["1P 3M 5P 7M 9A 11A","","maj7#9#11"],["1P 3M 5P 7M 9M 11A 13M","","M13#11 maj13#11 M13+4 M13#4"],["1P 3M 5P 7M 9m","","M7b9"],["1P 3M 5P 7m 11A 13m","","7#11b13 7b5b13"],["1P 3M 5P 7m 13M","","7add6 67 7add13"],["1P 3M 5P 7m 9A 11A","","7#9#11 7b5#9"],["1P 3M 5P 7m 9A 11A 13M","","13#9#11"],["1P 3M 5P 7m 9A 11A 13m","","7#9#11b13"],["1P 3M 5P 7m 9A 13M","","13#9 13#9_"],["1P 3M 5P 7m 9A 13m","","7#9b13"],["1P 3M 5P 7m 9M 11A","","9#11 9+4 9#4 9#11_ 9#4_"],["1P 3M 5P 7m 9M 11A 13M","","13#11 13+4 13#4"],["1P 3M 5P 7m 9M 11A 13m","","9#11b13 9b5b13"],["1P 3M 5P 7m 9m 11A","","7b9#11 7b5b9"],["1P 3M 5P 7m 9m 11A 13M","","13b9#11"],["1P 3M 5P 7m 9m 11A 13m","","7b9b13#11 7b9#11b13 7b5b9b13"],["1P 3M 5P 7m 9m 13M","","13b9"],["1P 3M 5P 7m 9m 13m","","7b9b13"],["1P 3M 5P 7m 9m 9A","","7b9#9"],["1P 3M 5P 9M","","Madd9 2 add9 add2"],["1P 3M 5P 9m","","Maddb9"],["1P 3M 5d","","Mb5"],["1P 3M 5d 6M 7m 9M","","13b5"],["1P 3M 5d 7M","","M7b5"],["1P 3M 5d 7M 9M","","M9b5"],["1P 3M 5d 7m","","7b5"],["1P 3M 5d 7m 9M","","9b5"],["1P 3M 7m","","7no5"],["1P 3M 7m 13m","","7b13"],["1P 3M 7m 9M","","9no5"],["1P 3M 7m 9M 13M","","13no5"],["1P 3M 7m 9M 13m","","9b13"],["1P 3m 4P 5P","","madd4"],["1P 3m 5A","","m#5 m+ mb6"],["1P 3m 5P 6M 9M","","m69 _69"],["1P 3m 5P 6m 7M","","mMaj7b6"],["1P 3m 5P 6m 7M 9M","","mMaj9b6"],["1P 3m 5P 7M 9M","","mMaj9 -Maj9"],["1P 3m 5P 7m 11P","","m7add11 m7add4"],["1P 3m 5P 9M","","madd9"],["1P 3m 5d 6M 7M","","o7M7"],["1P 3m 5d 7M","","oM7"],["1P 3m 6m 7M","","mb6M7"],["1P 3m 6m 7m","","m7#5"],["1P 3m 6m 7m 9M","","m9#5"],["1P 3m 6m 7m 9M 11P","","m11A"],["1P 3m 6m 9m","","mb6b9"],["1P 3m 7m 12d 2M","","m9b5 h9 -9b5"],["1P 3m 7m 12d 2M 4P","","m11b5 h11 _11b5"],["1P 4P 5A 7M","","M7#5sus4"],["1P 4P 5A 7M 9M","","M9#5sus4"],["1P 4P 5A 7m","","7#5sus4"],["1P 4P 5P 7M","","M7sus4"],["1P 4P 5P 7M 9M","","M9sus4"],["1P 4P 5P 7m 9M","","9sus4 9sus"],["1P 4P 5P 7m 9M 13M","","13sus4 13sus"],["1P 4P 5P 7m 9m 13m","","7sus4b9b13 7b9b13sus4"],["1P 4P 7m 10m","","4 quartal"],["1P 5P 7m 9m 11P","","11b9"]].forEach(([n,t,e])=>cn(n.split(" "),e.split(" "),t)),on.sort((n,t)=>n.setNum-t.setNum);var Pn=Object.freeze({__proto__:null,add:cn,addAlias:sn,chordType:function(n){return console.warn("ChordDictionary.chordType is deprecated. Use ChordDictionary.get instead"),rn(n)},clear:function(){on=[],mn={}},entries:an,get:rn,keys:function(){return Object.keys(mn)}});const Mn={...$,intervals:[],aliases:[]};let un=[],ln={};function dn(n){return ln[n]||Mn}function pn(){return un.slice()}function fn(n,t,e=[]){const o={...L(n),name:t,intervals:n,aliases:e};return un.push(o),ln[o.name]=o,ln[o.setNum]=o,ln[o.chroma]=o,o.aliases.forEach(n=>hn(o,n)),o}function hn(n,t){ln[t]=n}[["1P 2M 3M 5P 6M","major pentatonic","pentatonic"],["1P 3M 4P 5P 7M","ionian pentatonic"],["1P 3M 4P 5P 7m","mixolydian pentatonic","indian"],["1P 2M 4P 5P 6M","ritusen"],["1P 2M 4P 5P 7m","egyptian"],["1P 3M 4P 5d 7m","neopolitan major pentatonic"],["1P 3m 4P 5P 6m","vietnamese 1"],["1P 2m 3m 5P 6m","pelog"],["1P 2m 4P 5P 6m","kumoijoshi"],["1P 2M 3m 5P 6m","hirajoshi"],["1P 2m 4P 5d 7m","iwato"],["1P 2m 4P 5P 7m","in-sen"],["1P 3M 4A 5P 7M","lydian pentatonic","chinese"],["1P 3m 4P 6m 7m","malkos raga"],["1P 3m 4P 5d 7m","locrian pentatonic","minor seven flat five pentatonic"],["1P 3m 4P 5P 7m","minor pentatonic","vietnamese 2"],["1P 3m 4P 5P 6M","minor six pentatonic"],["1P 2M 3m 5P 6M","flat three pentatonic","kumoi"],["1P 2M 3M 5P 6m","flat six pentatonic"],["1P 2m 3M 5P 6M","scriabin"],["1P 3M 5d 6m 7m","whole tone pentatonic"],["1P 3M 4A 5A 7M","lydian #5P pentatonic"],["1P 3M 4A 5P 7m","lydian dominant pentatonic"],["1P 3m 4P 5P 7M","minor #7M pentatonic"],["1P 3m 4d 5d 7m","super locrian pentatonic"],["1P 2M 3m 4P 5P 7M","minor hexatonic"],["1P 2A 3M 5P 5A 7M","augmented"],["1P 3m 4P 5d 5P 7m","minor blues","blues"],["1P 2M 3m 3M 5P 6M","major blues"],["1P 2M 4P 5P 6M 7m","piongio"],["1P 2m 3M 4A 6M 7m","prometheus neopolitan"],["1P 2M 3M 4A 6M 7m","prometheus"],["1P 2m 3M 5d 6m 7m","mystery #1"],["1P 2m 3M 4P 5A 6M","six tone symmetric"],["1P 2M 3M 4A 5A 7m","whole tone"],["1P 2M 3M 4P 5d 6m 7m","locrian major","arabian"],["1P 2m 3M 4A 5P 6m 7M","double harmonic lydian"],["1P 2M 3m 4P 5P 6m 7M","harmonic minor"],["1P 2m 3m 3M 5d 6m 7m","altered","super locrian","diminished whole tone","pomeroy"],["1P 2M 3m 4P 5d 6m 7m","locrian #2","half-diminished",'"aeolian b5'],["1P 2M 3M 4P 5P 6m 7m","mixolydian b6","melodic minor fifth mode","hindu"],["1P 2M 3M 4A 5P 6M 7m","lydian dominant","lydian b7","overtone"],["1P 2M 3M 4A 5P 6M 7M","lydian"],["1P 2M 3M 4A 5A 6M 7M","lydian augmented"],["1P 2m 3m 4P 5P 6M 7m","dorian b2","phrygian #6","melodic minor second mode"],["1P 2M 3m 4P 5P 6M 7M","melodic minor"],["1P 2m 3m 4P 5d 6m 7m","locrian"],["1P 2m 3m 4d 5d 6m 7d","ultralocrian","superlocrian bb7","·superlocrian diminished"],["1P 2m 3m 4P 5d 6M 7m","locrian 6","locrian natural 6","locrian sharp 6"],["1P 2A 3M 4P 5P 5A 7M","augmented heptatonic"],["1P 2M 3m 5d 5P 6M 7m","romanian minor"],["1P 2M 3m 4A 5P 6M 7m","dorian #4"],["1P 2M 3m 4A 5P 6M 7M","lydian diminished"],["1P 2m 3m 4P 5P 6m 7m","phrygian"],["1P 2M 3M 4A 5A 7m 7M","leading whole tone"],["1P 2M 3M 4A 5P 6m 7m","lydian minor"],["1P 2m 3M 4P 5P 6m 7m","phrygian dominant","spanish","phrygian major"],["1P 2m 3m 4P 5P 6m 7M","balinese"],["1P 2m 3m 4P 5P 6M 7M","neopolitan major"],["1P 2M 3m 4P 5P 6m 7m","aeolian","minor"],["1P 2M 3M 4P 5P 6m 7M","harmonic major"],["1P 2m 3M 4P 5P 6m 7M","double harmonic major","gypsy"],["1P 2M 3m 4P 5P 6M 7m","dorian"],["1P 2M 3m 4A 5P 6m 7M","hungarian minor"],["1P 2A 3M 4A 5P 6M 7m","hungarian major"],["1P 2m 3M 4P 5d 6M 7m","oriental"],["1P 2m 3m 3M 4A 5P 7m","flamenco"],["1P 2m 3m 4A 5P 6m 7M","todi raga"],["1P 2M 3M 4P 5P 6M 7m","mixolydian","dominant"],["1P 2m 3M 4P 5d 6m 7M","persian"],["1P 2M 3M 4P 5P 6M 7M","major","ionian"],["1P 2m 3M 5d 6m 7m 7M","enigmatic"],["1P 2M 3M 4P 5A 6M 7M","major augmented","major #5","ionian augmented","ionian #5"],["1P 2A 3M 4A 5P 6M 7M","lydian #9"],["1P 2m 3M 4P 4A 5P 6m 7M","purvi raga"],["1P 2m 3m 3M 4P 5P 6m 7m","spanish heptatonic"],["1P 2M 3M 4P 5P 6M 7m 7M","bebop"],["1P 2M 3m 3M 4P 5P 6M 7m","bebop minor"],["1P 2M 3M 4P 5P 5A 6M 7M","bebop major"],["1P 2m 3m 4P 5d 5P 6m 7m","bebop locrian"],["1P 2M 3m 4P 5P 6m 7m 7M","minor bebop"],["1P 2M 3m 4P 5d 6m 6M 7M","diminished","whole-half diminished"],["1P 2M 3M 4P 5d 5P 6M 7M","ichikosucho"],["1P 2M 3m 4P 5P 6m 6M 7M","minor six diminished"],["1P 2m 3m 3M 4A 5P 6M 7m","half-whole diminished","dominant diminished"],["1P 3m 3M 4P 5P 6M 7m 7M","kafi raga"],["1P 2M 3m 3M 4P 5d 5P 6M 7m","composite blues"],["1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M","chromatic"]].forEach(([n,t,...e])=>fn(n.split(" "),t,e));var bn=Object.freeze({__proto__:null,NoScaleType:Mn,add:fn,addAlias:hn,clear:function(){un=[],ln={}},entries:pn,get:dn,keys:function(){return Object.keys(ln)},scaleType:function(n){return console.warn("ScaleDictionary.scaleType is deprecated. Use ScaleDictionary.get instead"),dn(n)}});const yn={empty:!0,name:"",type:"",tonic:null,setNum:NaN,quality:"Unknown",chroma:"",normalized:"",aliases:[],notes:[],intervals:[]},An=/^(6|64|7|9|11|13)$/;function gn(n){const[t,e,o,m]=d(n);return""===t?["",n]:"A"===t&&"ug"===m?["","aug"]:m||"4"!==o&&"5"!==o?An.test(o)?[t+e,o+m]:[t+e+o,m]:[t+e,o]}function jn(n){const{type:t,tonic:e}=function(n){if(!n||!n.length)return{};const t=Array.isArray(n)?n:gn(n),e=u(t[0]).name,o=rn(t[1]);return o.empty?e&&"string"==typeof n?{tonic:"",type:rn(n)}:{}:{tonic:e,type:o}}(n);if(!t||t.empty)return yn;const o=e?t.intervals.map(n=>v(e,n)):[],m=e?e+" "+t.name:t.name;return{...t,name:m,type:t.name,tonic:e||"",notes:o}}var In=Object.freeze({__proto__:null,chord:jn,chordScales:function(n){const t=Y(jn(n).chroma);return pn().filter(n=>t(n.chroma)).map(n=>n.name)},extended:function(n){const t=jn(n),e=Y(t.chroma);return an().filter(n=>e(n.chroma)).map(n=>t.tonic+n.aliases[0])},reduced:function(n){const t=jn(n),e=X(t.chroma);return an().filter(n=>e(n.chroma)).map(n=>t.tonic+n.aliases[0])},tokenize:gn,transpose:function(n,t){const[e,o]=gn(n);return e?v(e,t)+o:name}});const _n=[1,2,2,3,3,4,5,5,6,6,7,7],vn="P m M m M P d P m M m M".split(" ");function Nn(n){return(t,e)=>{const o=g(t).coord,m=g(e).coord;if(o&&m){return I(n(o,m)).name}}}const Vn=Nn((n,t)=>[n[0]+t[0],n[1]+t[1]]),Dn=Nn((n,t)=>[n[0]-t[0],n[1]-t[1]]);var xn=Object.freeze({__proto__:null,add:Vn,fromSemitones:function(n){const t=n<0?-1:1,e=Math.abs(n),o=e%12,m=Math.floor(e/12);return t*(_n[o]+7*m)+vn[o]},invert:function(n){const t=g(n);return t.empty?"":g({step:(7-t.step)%7,alt:"perfectable"===t.type?-t.alt:-(t.alt+1),oct:t.oct,dir:t.dir}).name},names:function(){return"1P 2M 3M 4P 5P 6m 7m".split(" ")},simplify:function(n){const t=g(n);return t.empty?"":t.simple+t.q},substract:Dn,tokenize:y});function Tn(n){return+n>=0&&+n<=127}function zn(n){if(Tn(n))return+n;const t=u(n);return t.empty?null:t.midi}const On=Math.log(2),Sn=Math.log(440);const Cn="C C# D D# E F F# G G# A A# B".split(" "),kn="C Db D Eb E F Gb G Ab A Bb B".split(" ");function wn(n,t={}){n=Math.round(n);const e=(!0===t.sharps?Cn:kn)[n%12];return t.pitchClass?e:e+(Math.floor(n/12)-1)}var En=Object.freeze({__proto__:null,freqToMidi:function(n){const t=12*(Math.log(n)-Sn)/On+69;return Math.round(100*t)/100},isMidi:Tn,midiToFreq:function(n,t=440){return Math.pow(2,(n-69)/12)*t},midiToNoteName:wn,toMidi:zn});const qn=n=>t=>{const e=u(t);if(e.empty)return"";const o=n?e.alt>0:e.alt<0,m=null===e.midi;return wn(e.midi||e.chroma,{sharps:o,pitchClass:m})},Fn=qn(!0),$n=qn(!1);function Un(n,t){const e=u(n);if(e.empty)return"";const[o,m]=e.coord;return p(void 0===m?[o+t]:[o+t,m]).name}var Rn=Object.freeze({__proto__:null,enharmonic:$n,simplify:Fn,transposeBy:n=>t=>v(t,n),transposeFifths:Un,transposeFrom:n=>t=>v(n,t),tokenize:d});const Gn={empty:!0,name:"",chordType:""},Bn={};function Kn(e){return"string"==typeof e?Bn[e]||(Bn[e]=function(n){const[t,e,o,m]=Hn(n);if(!o)return Gn;const r=o.toUpperCase(),a=Qn.indexOf(r),i=M(e);return{empty:!1,name:t,roman:o,interval:g({step:a,alt:i,dir:1}).name,acc:e,chordType:m,alt:i,step:a,major:o===r,oct:0,dir:1}}(e)):"number"==typeof e?Kn(Qn[e]||""):t(e)?Kn(P((o=e).alt)+Qn[o.step]):n(e)?Kn(e.name):Gn;var o}const Ln=/^(#{1,}|b{1,}|x{1,}|)(IV|I{1,3}|VI{0,2}|iv|i{1,3}|vi{0,2})([^IViv]*)$/;function Hn(n){return Ln.exec(n)||["","","",""]}const Jn="I II III IV V VI VII",Qn=Jn.split(" "),Wn=Jn.toLowerCase().split(" ");var Xn=Object.freeze({__proto__:null,names:function(n=!0){return(n?Qn:Wn).slice()},romanNumeral:Kn,tokenize:Hn});const Yn=n=>(t,e="")=>t.map((t,o)=>"-"!==t?n[o]+e+t:"");function Zn(n,t,e,o){return m=>{const r=n.split(" "),a=r.map(n=>Kn(n).interval||""),i=a.map(n=>v(m,n)),c=Yn(i);return{tonic:m,grades:r,intervals:a,scale:i,chords:c(t.split(" ")),chordsHarmonicFunction:e.split(" "),chordScales:c(o.split(",")," ")}}}const nt=(n,t)=>{const e=u(n),o=u(t);return e.empty||o.empty?0:o.coord[0]-e.coord[0]},tt=Zn("I II III IV V VI VII","maj7 m7 m7 maj7 7 m7 m7b5","T SD T SD D T D","major,dorian,phrygian,lydian,mixolydian,minor,locrian"),et=Zn("I II bIII IV V bVI bVII","m7 m7b5 maj7 m7 m7 maj7 7","T SD T SD D SD SD","minor,locrian,major,dorian,phrygian,lydian,mixolydian"),ot=Zn("I II bIII IV V bVI VII","mmaj7 m7b5 +maj7 m7 7 maj7 mo7","T SD T SD D SD D","harmonic minor,locrian 6,major augmented,lydian diminished,phrygian dominant,lydian #9,ultralocrian"),mt=Zn("I II bIII IV V VI VII","m6 m7 +maj7 7 7 m7b5 m7b5","T SD T SD D - -","melodic minor,dorian b2,lydian augmented,lydian dominant,mixolydian b6,locrian #2,altered");var rt=Object.freeze({__proto__:null,majorKey:function(n){const t=tt(n),e=nt("C",n),o=Yn(t.scale);return{...t,type:"major",minorRelative:v(n,"-3m"),alteration:e,keySignature:P(e),secondaryDominants:o("- VI7 VII7 I7 II7 III7 -".split(" ")),secondaryDominantsMinorRelative:o("- IIIm7b5 IV#m7 Vm7 VIm7 VIIm7b5 -".split(" ")),substituteDominants:o("- bIII7 IV7 bV7 bVI7 bVII7 -".split(" ")),substituteDominantsMinorRelative:o("- IIIm7 Im7 IIbm7 VIm7 IVm7 -".split(" "))}},majorTonicFromKeySignature:function(n){return"number"==typeof n?Un("C",n):"string"==typeof n&&/^b+|#+$/.test(n)?Un("C",M(n)):null},minorKey:function(n){const t=nt("C",n)-3;return{type:"minor",tonic:n,relativeMajor:v(n,"3m"),alteration:t,keySignature:P(t),natural:et(n),harmonic:ot(n),melodic:mt(n)}}});const at={...$,name:"",alt:0,modeNum:NaN,triad:"",seventh:"",aliases:[]},it=[[0,2773,0,"ionian","","Maj7","major"],[1,2902,2,"dorian","m","m7"],[2,3418,4,"phrygian","m","m7"],[3,2741,-1,"lydian","","Maj7"],[4,2774,1,"mixolydian","","7"],[5,2906,3,"aeolian","m","m7","minor"],[6,3434,5,"locrian","dim","m7b5"]].map((function(n){const[t,e,o,m,r,a,i]=n,c=i?[i]:[],s=Number(e).toString(2);return{empty:!1,intervals:J(s),modeNum:t,chroma:s,normalized:s,name:m,setNum:e,alt:o,triad:r,seventh:a,aliases:c}})),ct={};it.forEach(n=>{ct[n.name]=n,n.aliases.forEach(t=>{ct[t]=n})});var st=Object.freeze({__proto__:null,entries:function(){return it.slice()},mode:function n(t){return"string"==typeof t?ct[t.toLowerCase()]||at:t&&t.name?n(t.name):at}});var Pt=Object.freeze({__proto__:null,fromRomanNumerals:function(n,t){return t.map(Kn).map(t=>v(n,g(t))+t.chordType)},toRomanNumerals:function(n,t){return t.map(t=>{const[e,o]=gn(t);return Kn(g(N(n,e))).name+o})}});function Mt(n){const t=w(n.map(zn));return n.length&&t.length===n.length?t.reduce((n,t)=>{const e=n[n.length-1];return n.concat(C(e,t).slice(1))},[t[0]]):[]}var ut=Object.freeze({__proto__:null,chromatic:function(n,t){return Mt(n).map(n=>wn(n,t))},numeric:Mt});const lt={empty:!0,name:"",type:"",tonic:null,setNum:NaN,chroma:"",normalized:"",aliases:[],notes:[],intervals:[]};function dt(n){if("string"!=typeof n)return["",""];const t=n.indexOf(" "),e=u(n.substring(0,t));if(e.empty){const t=u(n);return t.empty?["",n]:[t.name,""]}const o=n.substring(e.name.length+1);return[e.name,o.length?o:""]}function pt(n){const t=Array.isArray(n)?n:dt(n),e=u(t[0]).name,o=dn(t[1]);if(o.empty)return lt;const m=o.name,r=e?o.intervals.map(n=>v(e,n)):[],a=e?e+" "+m:m;return{...o,name:a,type:m,tonic:e,notes:r}}var ft,ht,bt=Object.freeze({__proto__:null,extended:function(n){const t=Y(pt(n).chroma);return pn().filter(n=>t(n.chroma)).map(n=>n.name)},modeNames:function(n){const t=pt(n);if(t.empty)return[];const e=t.tonic?t.notes:t.intervals;return W(t.chroma).map((n,t)=>{const o=pt(n).name;return o?[e[t],o]:["",""]}).filter(n=>n[0])},reduced:function(n){const t=X(pt(n).chroma);return pn().filter(n=>t(n.chroma)).map(n=>n.name)},scale:pt,scaleChords:function(n){const t=X(pt(n).chroma);return an().filter(n=>t(n.chroma)).map(n=>n.aliases[0])},scaleNotes:function(n){const t=n.map(n=>u(n).pc).filter(n=>n),e=t[0],o=q(t);return k(o.indexOf(e),o)},tokenize:dt}),yt=(function(n,t){!function(n,t,e,o,m,r,a,i,c,s,P,M,u,l,d,p,f){var h=r;Object.keys(r).forEach((function(t){"default"!==t&&Object.defineProperty(n,t,{enumerable:!0,get:function(){return r[t]}})})),n.AbcNotation=t,n.Array=e,n.Chord=o,n.ChordDictionary=m,n.Core=r,n.Interval=a,n.Key=i,n.Midi=c,n.Mode=s,n.Note=P,n.PcSet=M,n.Progression=u,n.Range=l,n.RomanNumeral=d,n.Scale=p,n.ScaleDictionary=f,n.Tonal=h,Object.defineProperty(n,"__esModule",{value:!0})}(t,S,F,In,Pn,V,xn,rt,En,st,Rn,tn,Pt,ut,Xn,bt,bn)}(ft={exports:{}},ft.exports),ft.exports);return(ht=yt)&&ht.__esModule&&Object.prototype.hasOwnProperty.call(ht,"default")?ht.default:ht}(); +var Tonal=function(){"use strict";"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;const n=(n,t)=>Array(Math.abs(t)+1).join(n);function t(n,t,e){return function(...o){return console.warn(`${n} is deprecated. Use ${t}.`),e.apply(this,o)}}function e(n){return null!==n&&"object"==typeof n&&"string"==typeof n.name}function o(n){return null!==n&&"object"==typeof n&&"number"==typeof n.step&&"number"==typeof n.alt}const r=[0,2,4,-1,1,3,5],m=r.map(n=>Math.floor(7*n/12));function a(n){const{step:t,alt:e,oct:o,dir:a=1}=n,i=r[t]+7*e;return void 0===o?[a*i]:[a*i,a*(o-m[t]-4*e)]}const i=[3,0,4,1,5,2,6];function c(n){const[t,e,o]=n,r=i[function(n){const t=(n+1)%7;return t<0?7+t:t}(t)],a=Math.floor((t+1)/7);return void 0===e?{step:r,alt:a,dir:o}:{step:r,alt:a,oct:e+4*a+m[r],dir:o}}const s={empty:!0,name:"",pc:"",acc:""},u=new Map,P=n=>"CDEFGAB".charAt(n),l=t=>t<0?n("b",-t):n("#",t),M=n=>"b"===n[0]?-n.length:n.length;function d(n){const t=u.get(n);if(t)return t;const r="string"==typeof n?function(n){const t=p(n);if(""===t[0]||""!==t[3])return s;const e=t[0],o=t[1],r=t[2],m=(e.charCodeAt(0)+3)%7,i=M(o),c=r.length?+r:void 0,u=a({step:m,alt:i,oct:c}),P=e+o+r,l=e+o,d=(y[m]+i+120)%12,f=void 0===c?-100:c,h=y[m]+i+12*(f+1),b=h>=0&&h<=127?h:null,g=void 0===c?null:440*Math.pow(2,(h-69)/12);return{empty:!1,acc:o,alt:i,chroma:d,coord:u,freq:g,height:h,letter:e,midi:b,name:P,oct:c,pc:l,step:m}}(n):o(n)?d(function(n){const{step:t,alt:e,oct:o}=n,r=P(t);if(!r)return"";const m=r+l(e);return o||0===o?m+o:m}(n)):e(n)?d(n.name):s;return u.set(n,r),r}const f=/^([a-gA-G]?)(#{1,}|b{1,}|x{1,}|)(-?\d*)\s*(.*)$/;function p(n){const t=f.exec(n);return[t[1].toUpperCase(),t[2].replace(/x/g,"##"),t[3],t[4]]}function h(n){return d(c(n))}const y=[0,2,4,5,7,9,11];const b={empty:!0,name:"",acc:""},g=new RegExp("^([-+]?\\d+)(d{1,4}|m|M|P|A{1,4})|(AA|A|P|M|m|d|dd)([-+]?\\d+)$");function A(n){const t=g.exec(`${n}`);return null===t?["",""]:t[1]?[t[1],t[2]]:[t[4],t[3]]}const I={};function v(t){return"string"==typeof t?I[t]||(I[t]=function(n){const t=A(n);if(""===t[0])return b;const e=+t[0],o=t[1],r=(Math.abs(e)-1)%7,m="PMMPPMM"[r];if("M"===m&&"P"===o)return b;const i="M"===m?"majorable":"perfectable",c=""+e+o,s=e<0?-1:1,u=8===e||-8===e?e:s*(r+1),P=function(n,t){return"M"===t&&"majorable"===n||"P"===t&&"perfectable"===n?0:"m"===t&&"majorable"===n?-1:/^A+$/.test(t)?t.length:/^d+$/.test(t)?-1*("perfectable"===n?t.length:t.length+1):0}(i,o),l=Math.floor((Math.abs(e)-1)/7),M=s*(j[r]+P+12*l),d=(s*(j[r]+P)%12+12)%12,f=a({step:r,alt:P,oct:l,dir:s});return{empty:!1,name:c,num:e,q:o,step:r,alt:P,dir:s,type:i,simple:u,semitones:M,chroma:d,coord:f,oct:l}}(t)):o(t)?v(function(t){const{step:e,alt:o,oct:r=0,dir:m}=t;if(!m)return"";return(m<0?"-":"")+(e+1+7*r)+function(t,e){return 0===e?"majorable"===t?"M":"P":-1===e&&"majorable"===t?"m":e>0?n("A",e):n("d","perfectable"===t?e:e+1)}("M"==="PMMPPMM"[e]?"majorable":"perfectable",o)}(t)):e(t)?v(t.name):b}const j=[0,2,4,5,7,9,11];function N(n){const[t,e=0]=n;return v(c(7*t+12*e<0?[-t,-e,-1]:[t,e,1]))}function w(n,t){const e=d(n),o=v(t);if(e.empty||o.empty)return"";const r=e.coord,m=o.coord;return h(1===r.length?[r[0]+m[0]]:[r[0]+m[0],r[1]+m[1]]).name}function T(n,t){const e=d(n),o=d(t);if(e.empty||o.empty)return"";const r=e.coord,m=o.coord,a=m[0]-r[0];return N([a,2===r.length&&2===m.length?m[1]-r[1]:-Math.floor(7*a/12)]).name}var V=Object.freeze({__proto__:null,accToAlt:M,altToAcc:l,coordToInterval:N,coordToNote:h,decode:c,deprecate:t,distance:T,encode:a,fillStr:n,interval:v,isNamed:e,isPitch:o,note:d,stepToLetter:P,tokenizeInterval:A,tokenizeNote:p,transpose:w});const S=(n,t)=>Array(t+1).join(n),C=/^(_{1,}|=|\^{1,}|)([abcdefgABCDEFG])([,']*)$/;function D(n){const t=C.exec(n);return t?[t[1],t[2],t[3]]:["","",""]}function x(n){const[t,e,o]=D(n);if(""===e)return"";let r=4;for(let n=0;n96?e.toUpperCase()+m+(r+1):e+m+r}function O(n){const t=d(n);if(t.empty||!t.oct)return"";const{letter:e,acc:o,oct:r}=t;return("b"===o[0]?o.replace(/b/g,"_"):o.replace(/#/g,"^"))+(r>4?e.toLowerCase():e)+(5===r?"":r>4?S("'",r-5):S(",",4-r))}var _={abcToScientificNotation:x,scientificToAbcNotation:O,tokenize:D,transpose:function(n,t){return O(w(x(n),t))},distance:function(n,t){return T(x(n),x(t))}};function k(n){return n.map(n=>d(n)).filter(n=>!n.empty).sort((n,t)=>n.height-t.height).map(n=>n.name)}var $=Object.freeze({__proto__:null,compact:function(n){return n.filter(n=>0===n||n)},permutations:function n(t){return 0===t.length?[[]]:n(t.slice(1)).reduce((n,e)=>n.concat(t.map((n,o)=>{const r=e.slice();return r.splice(o,0,t[0]),r})),[])},range:function(n,t){return n0===t||n!==e[t-1])}});function q(n,t){return n0===n||n)}var z={compact:F,permutations:function n(t){return 0===t.length?[[]]:n(t.slice(1)).reduce((n,e)=>n.concat(t.map((n,o)=>{const r=e.slice();return r.splice(o,0,t[0]),r})),[])},range:q,rotate:E,shuffle:function(n,t=Math.random){let e,o,r=n.length;for(;r;)e=Math.floor(t()*r--),o=n[r],n[r]=n[e],n[e]=o;return n}};const R={empty:!0,name:"",setNum:0,chroma:"000000000000",normalized:"000000000000",intervals:[]},U=n=>Number(n).toString(2),B=n=>parseInt(n,2),G=/^[01]{12}$/;function K(n){return G.test(n)}const L={[R.chroma]:R};function H(n){const t=K(n)?n:"number"==typeof(e=n)&&e>=0&&e<=4095?U(n):Array.isArray(n)?function(n){if(0===n.length)return R.chroma;let t;const e=[0,0,0,0,0,0,0,0,0,0,0,0];for(let o=0;on&&K(n.chroma))(n)?n.chroma:R.chroma;var e;return L[t]=L[t]||function(n){const t=B(n),e=function(n){const t=n.split("");return t.map((n,e)=>E(e,t).join(""))}(n).map(B).filter(n=>n>=2048).sort()[0],o=U(e),r=W(n);return{empty:!1,name:"",setNum:t,chroma:n,normalized:o,intervals:r}}(t)}const J=t("Pcset.pcset","Pcset.get",H),Q=["1P","2m","2M","3m","3M","4P","5d","5P","6m","6M","7m","7M"];function W(n){const t=[];for(let e=0;e<12;e++)"1"===n.charAt(e)&&t.push(Q[e]);return t}function X(n,t=!0){const e=H(n).chroma.split("");return F(e.map((n,o)=>{const r=E(o,e);return t&&"0"===r[0]?null:r.join("")}))}function Y(n){const t=H(n).setNum;return n=>{const e=H(n).setNum;return t&&t!==e&&(e&t)===e}}function Z(n){const t=H(n).setNum;return n=>{const e=H(n).setNum;return t&&t!==e&&(e|t)===e}}function nn(n){const t=H(n);return n=>{const e=d(n);return t&&!e.empty&&"1"===t.chroma.charAt(e.chroma)}}var tn={get:H,chroma:n=>H(n).chroma,num:n=>H(n).setNum,intervals:n=>H(n).intervals,chromas:function(){return q(2048,4095).map(U)},isSupersetOf:Z,isSubsetOf:Y,isNoteIncludedIn:nn,isEqual:function(n,t){return H(n).setNum===H(t).setNum},filter:function(n){const t=nn(n);return n=>n.filter(t)},modes:X,pcset:J};const en={...R,name:"",quality:"Unknown",intervals:[],aliases:[]};let on=[],rn={};function mn(n){return rn[n]||en}const an=t("ChordType.chordType","ChordType.get",mn);function cn(){return on.slice()}const sn=t("ChordType.entries","ChordType.all",cn);function un(n,t,e){const o=function(n){const t=t=>-1!==n.indexOf(t);return t("5A")?"Augmented":t("3M")?"Major":t("5d")?"Diminished":t("3m")?"Minor":"Unknown"}(n),r={...H(n),name:e||"",quality:o,intervals:n,aliases:t};on.push(r),r.name&&(rn[r.name]=r),rn[r.setNum]=r,rn[r.chroma]=r,r.aliases.forEach(n=>function(n,t){rn[t]=n}(r,n))}[["1P 3M 5P","major","M "],["1P 3M 5P 7M","major seventh","maj7 Δ ma7 M7 Maj7"],["1P 3M 5P 7M 9M","major ninth","maj9 Δ9"],["1P 3M 5P 7M 9M 13M","major thirteenth","maj13 Maj13"],["1P 3M 5P 6M","sixth","6 add6 add13 M6"],["1P 3M 5P 6M 9M","sixth/ninth","6/9 69"],["1P 3M 5P 7M 11A","lydian","maj#4 Δ#4 Δ#11"],["1P 3M 6m 7M","major seventh b6","M7b6"],["1P 3m 5P","minor","m min -"],["1P 3m 5P 7m","minor seventh","m7 min7 mi7 -7"],["1P 3m 5P 7M","minor/major seventh","m/ma7 m/maj7 mM7 m/M7 -Δ7 mΔ"],["1P 3m 5P 6M","minor sixth","m6"],["1P 3m 5P 7m 9M","minor ninth","m9"],["1P 3m 5P 7m 9M 11P","minor eleventh","m11"],["1P 3m 5P 7m 9M 13M","minor thirteenth","m13"],["1P 3m 5d","diminished","dim ° o"],["1P 3m 5d 7d","diminished seventh","dim7 °7 o7"],["1P 3m 5d 7m","half-diminished","m7b5 ø"],["1P 3M 5P 7m","dominant seventh","7 dom"],["1P 3M 5P 7m 9M","dominant ninth","9"],["1P 3M 5P 7m 9M 13M","dominant thirteenth","13"],["1P 3M 5P 7m 11A","lydian dominant seventh","7#11 7#4"],["1P 3M 5P 7m 9m","dominant b9","7b9"],["1P 3M 5P 7m 9A","dominant #9","7#9"],["1P 3M 7m 9m","altered","alt7"],["1P 4P 5P","suspended 4th","sus4"],["1P 2M 5P","suspended 2nd","sus2"],["1P 4P 5P 7m","suspended 4th seventh","7sus4"],["1P 5P 7m 9M 11P","eleventh","11"],["1P 4P 5P 7m 9m","suspended 4th b9","b9sus phryg"],["1P 5P","fifth","5"],["1P 3M 5A","augmented","aug + +5"],["1P 3M 5A 7M","augmented seventh","maj7#5 maj7+5"],["1P 3M 5P 7M 9M 11A","major #11 (lydian)","maj9#11 Δ9#11"],["1P 2M 4P 5P","","sus24 sus4add9"],["1P 3M 13m","","Mb6"],["1P 3M 5A 7M 9M","","maj9#5 Maj9#5"],["1P 3M 5A 7m","","7#5 +7 7aug aug7"],["1P 3M 5A 7m 9A","","7#5#9 7alt 7#5#9_ 7#9b13_"],["1P 3M 5A 7m 9M","","9#5 9+"],["1P 3M 5A 7m 9M 11A","","9#5#11"],["1P 3M 5A 7m 9m","","7#5b9"],["1P 3M 5A 7m 9m 11A","","7#5b9#11"],["1P 3M 5A 9A","","+add#9"],["1P 3M 5A 9M","","M#5add9 +add9"],["1P 3M 5P 6M 11A","","M6#11 M6b5 6#11 6b5"],["1P 3M 5P 6M 7M 9M","","M7add13"],["1P 3M 5P 6M 9M 11A","","69#11"],["1P 3M 5P 6m 7m","","7b6"],["1P 3M 5P 7M 9A 11A","","maj7#9#11"],["1P 3M 5P 7M 9M 11A 13M","","M13#11 maj13#11 M13+4 M13#4"],["1P 3M 5P 7M 9m","","M7b9"],["1P 3M 5P 7m 11A 13m","","7#11b13 7b5b13"],["1P 3M 5P 7m 13M","","7add6 67 7add13"],["1P 3M 5P 7m 9A 11A","","7#9#11 7b5#9"],["1P 3M 5P 7m 9A 11A 13M","","13#9#11"],["1P 3M 5P 7m 9A 11A 13m","","7#9#11b13"],["1P 3M 5P 7m 9A 13M","","13#9 13#9_"],["1P 3M 5P 7m 9A 13m","","7#9b13"],["1P 3M 5P 7m 9M 11A","","9#11 9+4 9#4 9#11_ 9#4_"],["1P 3M 5P 7m 9M 11A 13M","","13#11 13+4 13#4"],["1P 3M 5P 7m 9M 11A 13m","","9#11b13 9b5b13"],["1P 3M 5P 7m 9m 11A","","7b9#11 7b5b9"],["1P 3M 5P 7m 9m 11A 13M","","13b9#11"],["1P 3M 5P 7m 9m 11A 13m","","7b9b13#11 7b9#11b13 7b5b9b13"],["1P 3M 5P 7m 9m 13M","","13b9"],["1P 3M 5P 7m 9m 13m","","7b9b13"],["1P 3M 5P 7m 9m 9A","","7b9#9"],["1P 3M 5P 9M","","Madd9 2 add9 add2"],["1P 3M 5P 9m","","Maddb9"],["1P 3M 5d","","Mb5"],["1P 3M 5d 6M 7m 9M","","13b5"],["1P 3M 5d 7M","","M7b5"],["1P 3M 5d 7M 9M","","M9b5"],["1P 3M 5d 7m","","7b5"],["1P 3M 5d 7m 9M","","9b5"],["1P 3M 7m","","7no5"],["1P 3M 7m 13m","","7b13"],["1P 3M 7m 9M","","9no5"],["1P 3M 7m 9M 13M","","13no5"],["1P 3M 7m 9M 13m","","9b13"],["1P 3m 4P 5P","","madd4"],["1P 3m 5A","","m#5 m+ mb6"],["1P 3m 5P 6M 9M","","m69 _69"],["1P 3m 5P 6m 7M","","mMaj7b6"],["1P 3m 5P 6m 7M 9M","","mMaj9b6"],["1P 3m 5P 7M 9M","","mMaj9 -Maj9"],["1P 3m 5P 7m 11P","","m7add11 m7add4"],["1P 3m 5P 9M","","madd9"],["1P 3m 5d 6M 7M","","o7M7"],["1P 3m 5d 7M","","oM7"],["1P 3m 6m 7M","","mb6M7"],["1P 3m 6m 7m","","m7#5"],["1P 3m 6m 7m 9M","","m9#5"],["1P 3m 6m 7m 9M 11P","","m11A"],["1P 3m 6m 9m","","mb6b9"],["1P 3m 7m 12d 2M","","m9b5 h9 -9b5"],["1P 3m 7m 12d 2M 4P","","m11b5 h11 _11b5"],["1P 4P 5A 7M","","M7#5sus4"],["1P 4P 5A 7M 9M","","M9#5sus4"],["1P 4P 5A 7m","","7#5sus4"],["1P 4P 5P 7M","","M7sus4"],["1P 4P 5P 7M 9M","","M9sus4"],["1P 4P 5P 7m 9M","","9sus4 9sus"],["1P 4P 5P 7m 9M 13M","","13sus4 13sus"],["1P 4P 5P 7m 9m 13m","","7sus4b9b13 7b9b13sus4"],["1P 4P 7m 10m","","4 quartal"],["1P 5P 7m 9m 11P","","11b9"]].forEach(([n,t,e])=>un(n.split(" "),e.split(" "),t)),on.sort((n,t)=>n.setNum-t.setNum);var Pn={names:function(){return on.map(n=>n.name).filter(n=>n)},symbols:function(){return on.map(n=>n.aliases[0]).filter(n=>n)},get:mn,all:cn,add:un,removeAll:function(){on=[],rn={}},keys:function(){return Object.keys(rn)},entries:sn,chordType:an};const ln={weight:0,name:""};const Mn={...R,intervals:[],aliases:[]};let dn=[],fn={};function pn(){return dn.map(n=>n.name)}function hn(n){return fn[n]||Mn}const yn=t("ScaleDictionary.scaleType","ScaleType.get",hn);function bn(){return dn.slice()}const gn=t("ScaleDictionary.entries","ScaleType.all",bn);function An(n,t,e=[]){const o={...H(n),name:t,intervals:n,aliases:e};return dn.push(o),fn[o.name]=o,fn[o.setNum]=o,fn[o.chroma]=o,o.aliases.forEach(n=>function(n,t){fn[t]=n}(o,n)),o}[["1P 2M 3M 5P 6M","major pentatonic","pentatonic"],["1P 3M 4P 5P 7M","ionian pentatonic"],["1P 3M 4P 5P 7m","mixolydian pentatonic","indian"],["1P 2M 4P 5P 6M","ritusen"],["1P 2M 4P 5P 7m","egyptian"],["1P 3M 4P 5d 7m","neopolitan major pentatonic"],["1P 3m 4P 5P 6m","vietnamese 1"],["1P 2m 3m 5P 6m","pelog"],["1P 2m 4P 5P 6m","kumoijoshi"],["1P 2M 3m 5P 6m","hirajoshi"],["1P 2m 4P 5d 7m","iwato"],["1P 2m 4P 5P 7m","in-sen"],["1P 3M 4A 5P 7M","lydian pentatonic","chinese"],["1P 3m 4P 6m 7m","malkos raga"],["1P 3m 4P 5d 7m","locrian pentatonic","minor seven flat five pentatonic"],["1P 3m 4P 5P 7m","minor pentatonic","vietnamese 2"],["1P 3m 4P 5P 6M","minor six pentatonic"],["1P 2M 3m 5P 6M","flat three pentatonic","kumoi"],["1P 2M 3M 5P 6m","flat six pentatonic"],["1P 2m 3M 5P 6M","scriabin"],["1P 3M 5d 6m 7m","whole tone pentatonic"],["1P 3M 4A 5A 7M","lydian #5P pentatonic"],["1P 3M 4A 5P 7m","lydian dominant pentatonic"],["1P 3m 4P 5P 7M","minor #7M pentatonic"],["1P 3m 4d 5d 7m","super locrian pentatonic"],["1P 2M 3m 4P 5P 7M","minor hexatonic"],["1P 2A 3M 5P 5A 7M","augmented"],["1P 3m 4P 5d 5P 7m","minor blues","blues"],["1P 2M 3m 3M 5P 6M","major blues"],["1P 2M 4P 5P 6M 7m","piongio"],["1P 2m 3M 4A 6M 7m","prometheus neopolitan"],["1P 2M 3M 4A 6M 7m","prometheus"],["1P 2m 3M 5d 6m 7m","mystery #1"],["1P 2m 3M 4P 5A 6M","six tone symmetric"],["1P 2M 3M 4A 5A 7m","whole tone"],["1P 2M 3M 4P 5d 6m 7m","locrian major","arabian"],["1P 2m 3M 4A 5P 6m 7M","double harmonic lydian"],["1P 2M 3m 4P 5P 6m 7M","harmonic minor"],["1P 2m 3m 3M 5d 6m 7m","altered","super locrian","diminished whole tone","pomeroy"],["1P 2M 3m 4P 5d 6m 7m","locrian #2","half-diminished",'"aeolian b5'],["1P 2M 3M 4P 5P 6m 7m","mixolydian b6","melodic minor fifth mode","hindu"],["1P 2M 3M 4A 5P 6M 7m","lydian dominant","lydian b7","overtone"],["1P 2M 3M 4A 5P 6M 7M","lydian"],["1P 2M 3M 4A 5A 6M 7M","lydian augmented"],["1P 2m 3m 4P 5P 6M 7m","dorian b2","phrygian #6","melodic minor second mode"],["1P 2M 3m 4P 5P 6M 7M","melodic minor"],["1P 2m 3m 4P 5d 6m 7m","locrian"],["1P 2m 3m 4d 5d 6m 7d","ultralocrian","superlocrian bb7","·superlocrian diminished"],["1P 2m 3m 4P 5d 6M 7m","locrian 6","locrian natural 6","locrian sharp 6"],["1P 2A 3M 4P 5P 5A 7M","augmented heptatonic"],["1P 2M 3m 5d 5P 6M 7m","romanian minor"],["1P 2M 3m 4A 5P 6M 7m","dorian #4"],["1P 2M 3m 4A 5P 6M 7M","lydian diminished"],["1P 2m 3m 4P 5P 6m 7m","phrygian"],["1P 2M 3M 4A 5A 7m 7M","leading whole tone"],["1P 2M 3M 4A 5P 6m 7m","lydian minor"],["1P 2m 3M 4P 5P 6m 7m","phrygian dominant","spanish","phrygian major"],["1P 2m 3m 4P 5P 6m 7M","balinese"],["1P 2m 3m 4P 5P 6M 7M","neopolitan major"],["1P 2M 3m 4P 5P 6m 7m","aeolian","minor"],["1P 2M 3M 4P 5P 6m 7M","harmonic major"],["1P 2m 3M 4P 5P 6m 7M","double harmonic major","gypsy"],["1P 2M 3m 4P 5P 6M 7m","dorian"],["1P 2M 3m 4A 5P 6m 7M","hungarian minor"],["1P 2A 3M 4A 5P 6M 7m","hungarian major"],["1P 2m 3M 4P 5d 6M 7m","oriental"],["1P 2m 3m 3M 4A 5P 7m","flamenco"],["1P 2m 3m 4A 5P 6m 7M","todi raga"],["1P 2M 3M 4P 5P 6M 7m","mixolydian","dominant"],["1P 2m 3M 4P 5d 6m 7M","persian"],["1P 2M 3M 4P 5P 6M 7M","major","ionian"],["1P 2m 3M 5d 6m 7m 7M","enigmatic"],["1P 2M 3M 4P 5A 6M 7M","major augmented","major #5","ionian augmented","ionian #5"],["1P 2A 3M 4A 5P 6M 7M","lydian #9"],["1P 2m 3M 4P 4A 5P 6m 7M","purvi raga"],["1P 2m 3m 3M 4P 5P 6m 7m","spanish heptatonic"],["1P 2M 3M 4P 5P 6M 7m 7M","bebop"],["1P 2M 3m 3M 4P 5P 6M 7m","bebop minor"],["1P 2M 3M 4P 5P 5A 6M 7M","bebop major"],["1P 2m 3m 4P 5d 5P 6m 7m","bebop locrian"],["1P 2M 3m 4P 5P 6m 7m 7M","minor bebop"],["1P 2M 3m 4P 5d 6m 6M 7M","diminished","whole-half diminished"],["1P 2M 3M 4P 5d 5P 6M 7M","ichikosucho"],["1P 2M 3m 4P 5P 6m 6M 7M","minor six diminished"],["1P 2m 3m 3M 4A 5P 6M 7m","half-whole diminished","dominant diminished"],["1P 3m 3M 4P 5P 6M 7m 7M","kafi raga"],["1P 2M 3m 3M 4P 5d 5P 6M 7m","composite blues"],["1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M","chromatic"]].forEach(([n,t,...e])=>An(n.split(" "),t,e));var In={names:pn,get:hn,all:bn,add:An,removeAll:function(){dn=[],fn={}},keys:function(){return Object.keys(fn)},entries:gn,scaleType:yn};const vn={empty:!0,name:"",type:"",tonic:null,setNum:NaN,quality:"Unknown",chroma:"",normalized:"",aliases:[],notes:[],intervals:[]},jn=/^(6|64|7|9|11|13)$/;function Nn(n){const[t,e,o,r]=p(n);return""===t?["",n]:"A"===t&&"ug"===r?["","aug"]:r||"4"!==o&&"5"!==o?jn.test(o)?[t+e,o+r]:[t+e+o,r]:[t+e,o]}function wn(n){const{type:t,tonic:e}=function(n){if(!n||!n.length)return{};const t=Array.isArray(n)?n:Nn(n),e=d(t[0]).name,o=mn(t[1]);return o.empty?e&&"string"==typeof n?{tonic:"",type:mn(n)}:{}:{tonic:e,type:o}}(n);if(!t||t.empty)return vn;const o=e?t.intervals.map(n=>w(e,n)):[],r=e?e+" "+t.name:t.name;return{...t,name:r,type:t.name,tonic:e||"",notes:o}}var Tn={get:wn,detect:function(n){const t=n.map(n=>d(n).pc).filter(n=>n);return 0===d.length?[]:function(n,t){const e=n[0],o=d(e).chroma,r=(n=>{const t=n.reduce((n,t)=>{const e=d(t).chroma;return void 0!==e&&(n[e]=n[e]||d(t).name),n},{});return n=>t[n]})(n);return X(n,!1).map((n,m)=>{const a=mn(n).aliases[0];if(!a)return ln;const i=r(m);return m!==o?{weight:.5*t,name:`${i}${a}/${e}`}:{weight:1*t,name:`${i}${a}`}})}(t,1).filter(n=>n.weight).sort((n,t)=>t.weight-n.weight).map(n=>n.name)},chordScales:function(n){const t=Z(wn(n).chroma);return bn().filter(n=>t(n.chroma)).map(n=>n.name)},extended:function(n){const t=wn(n),e=Z(t.chroma);return cn().filter(n=>e(n.chroma)).map(n=>t.tonic+n.aliases[0])},reduced:function(n){const t=wn(n),e=Y(t.chroma);return cn().filter(n=>e(n.chroma)).map(n=>t.tonic+n.aliases[0])},tokenize:Nn,transpose:function(n,t){const[e,o]=Nn(n);return e?w(e,t)+o:name},chord:t("Chord.chord","Chord.get",wn)};const Vn=v;const Sn=[1,2,2,3,3,4,5,5,6,6,7,7],Cn="P m M m M P d P m M m M".split(" ");const Dn=T,xn=kn((n,t)=>[n[0]+t[0],n[1]+t[1]]),On=kn((n,t)=>[n[0]-t[0],n[1]-t[1]]);var _n={names:function(){return"1P 2M 3M 4P 5P 6m 7m".split(" ")},get:Vn,name:n=>v(n).name,num:n=>v(n).num,semitones:n=>v(n).semitones,quality:n=>v(n).q,fromSemitones:function(n){const t=n<0?-1:1,e=Math.abs(n),o=e%12,r=Math.floor(e/12);return t*(Sn[o]+7*r)+Cn[o]},distance:Dn,invert:function(n){const t=v(n);return t.empty?"":v({step:(7-t.step)%7,alt:"perfectable"===t.type?-t.alt:-(t.alt+1),oct:t.oct,dir:t.dir}).name},simplify:function(n){const t=v(n);return t.empty?"":t.simple+t.q},add:xn,addTo:n=>t=>xn(n,t),substract:On};function kn(n){return(t,e)=>{const o=v(t).coord,r=v(e).coord;if(o&&r){return N(n(o,r)).name}}}function $n(n){return+n>=0&&+n<=127}function qn(n){if($n(n))return+n;const t=d(n);return t.empty?null:t.midi}const En=Math.log(2),Fn=Math.log(440);const zn="C C# D D# E F F# G G# A A# B".split(" "),Rn="C Db D Eb E F Gb G Ab A Bb B".split(" ");function Un(n,t={}){n=Math.round(n);const e=(!0===t.sharps?zn:Rn)[n%12];return t.pitchClass?e:e+(Math.floor(n/12)-1)}var Bn={isMidi:$n,toMidi:qn,midiToFreq:function(n,t=440){return Math.pow(2,(n-69)/12)*t},midiToNoteName:Un,freqToMidi:function(n){const t=12*(Math.log(n)-Fn)/En+69;return Math.round(100*t)/100}};const Gn=["C","D","E","F","G","A","B"],Kn=n=>n.name,Ln=n=>n.map(d).filter(n=>!n.empty);const Hn=d;const Jn=w,Qn=w,Wn=n=>t=>Jn(t,n),Xn=Wn,Yn=n=>t=>Jn(n,t),Zn=Yn;function nt(n,t){const e=Hn(n);if(e.empty)return"";const[o,r]=e.coord;return h(void 0===r?[o+t]:[o+t,r]).name}const tt=nt,et=(n,t)=>n.height-t.height;function ot(n,t){return t=t||et,Ln(n).sort(t).map(Kn)}function rt(n){return ot(n,et).filter((n,t,e)=>0===t||n!==e[t-1])}const mt=it(!0),at=it(!1);function it(n){return t=>{const e=Hn(t);if(e.empty)return"";const o=n?e.alt>0:e.alt<0,r=null===e.midi;return Un(e.midi||e.chroma,{sharps:o,pitchClass:r})}}var ct={names:function(n){return void 0===n?Gn.slice():Array.isArray(n)?Ln(n).map(Kn):[]},get:Hn,name:n=>Hn(n).name,pitchClass:n=>Hn(n).pc,accidentals:n=>Hn(n).acc,octave:n=>Hn(n).oct,midi:n=>Hn(n).midi,ascending:et,descending:(n,t)=>t.height-n.height,sortedNames:ot,sortedUniqNames:rt,fromMidi:function(n){return Un(n)},fromMidiSharps:function(n){return Un(n,{sharps:!0})},freq:n=>Hn(n).freq,chroma:n=>Hn(n).chroma,transpose:Jn,tr:Qn,transposeBy:Wn,trBy:Xn,transposeFrom:Yn,trFrom:Zn,transposeFifths:nt,trFifths:tt,simplify:mt,enharmonic:at};const st={empty:!0,name:"",chordType:""},ut={};function Pt(n){return"string"==typeof n?ut[n]||(ut[n]=function(n){const[t,e,o,r]=(m=n,Mt.exec(m)||["","","",""]);var m;if(!o)return st;const a=o.toUpperCase(),i=ft.indexOf(a),c=M(e);return{empty:!1,name:t,roman:o,interval:v({step:i,alt:c,dir:1}).name,acc:e,chordType:r,alt:c,step:i,major:o===a,oct:0,dir:1}}(n)):"number"==typeof n?Pt(ft[n]||""):o(n)?Pt(l((t=n).alt)+ft[t.step]):e(n)?Pt(n.name):st;var t}const lt=t("RomanNumeral.romanNumeral","RomanNumeral.get",Pt);const Mt=/^(#{1,}|b{1,}|x{1,}|)(IV|I{1,3}|VI{0,2}|iv|i{1,3}|vi{0,2})([^IViv]*)$/;const dt="I II III IV V VI VII",ft=dt.split(" "),pt=dt.toLowerCase().split(" ");var ht={names:function(n=!0){return(n?ft:pt).slice()},get:Pt,romanNumeral:lt};const yt=n=>(t,e="")=>t.map((t,o)=>"-"!==t?n[o]+e+t:"");function bt(n,t,e,o){return r=>{const m=n.split(" "),a=m.map(n=>Pt(n).interval||""),i=a.map(n=>w(r,n)),c=yt(i);return{tonic:r,grades:m,intervals:a,scale:i,chords:c(t.split(" ")),chordsHarmonicFunction:e.split(" "),chordScales:c(o.split(",")," ")}}}const gt=(n,t)=>{const e=d(n),o=d(t);return e.empty||o.empty?0:o.coord[0]-e.coord[0]},At=bt("I II III IV V VI VII","maj7 m7 m7 maj7 7 m7 m7b5","T SD T SD D T D","major,dorian,phrygian,lydian,mixolydian,minor,locrian"),It=bt("I II bIII IV V bVI bVII","m7 m7b5 maj7 m7 m7 maj7 7","T SD T SD D SD SD","minor,locrian,major,dorian,phrygian,lydian,mixolydian"),vt=bt("I II bIII IV V bVI VII","mmaj7 m7b5 +maj7 m7 7 maj7 mo7","T SD T SD D SD D","harmonic minor,locrian 6,major augmented,lydian diminished,phrygian dominant,lydian #9,ultralocrian"),jt=bt("I II bIII IV V VI VII","m6 m7 +maj7 7 7 m7b5 m7b5","T SD T SD D - -","melodic minor,dorian b2,lydian augmented,lydian dominant,mixolydian b6,locrian #2,altered");var Nt={majorKey:function(n){const t=At(n),e=gt("C",n),o=yt(t.scale);return{...t,type:"major",minorRelative:w(n,"-3m"),alteration:e,keySignature:l(e),secondaryDominants:o("- VI7 VII7 I7 II7 III7 -".split(" ")),secondaryDominantsMinorRelative:o("- IIIm7b5 IV#m7 Vm7 VIm7 VIIm7b5 -".split(" ")),substituteDominants:o("- bIII7 IV7 bV7 bVI7 bVII7 -".split(" ")),substituteDominantsMinorRelative:o("- IIIm7 Im7 IIbm7 VIm7 IVm7 -".split(" "))}},majorTonicFromKeySignature:function(n){return"number"==typeof n?nt("C",n):"string"==typeof n&&/^b+|#+$/.test(n)?nt("C",M(n)):null},minorKey:function(n){const t=gt("C",n)-3;return{type:"minor",tonic:n,relativeMajor:w(n,"3m"),alteration:t,keySignature:l(t),natural:It(n),harmonic:vt(n),melodic:jt(n)}}};const wt={...R,name:"",alt:0,modeNum:NaN,triad:"",seventh:"",aliases:[]},Tt=[[0,2773,0,"ionian","","Maj7","major"],[1,2902,2,"dorian","m","m7"],[2,3418,4,"phrygian","m","m7"],[3,2741,-1,"lydian","","Maj7"],[4,2774,1,"mixolydian","","7"],[5,2906,3,"aeolian","m","m7","minor"],[6,3434,5,"locrian","dim","m7b5"]].map((function(n){const[t,e,o,r,m,a,i]=n,c=i?[i]:[],s=Number(e).toString(2);return{empty:!1,intervals:W(s),modeNum:t,chroma:s,normalized:s,name:r,setNum:e,alt:o,triad:m,seventh:a,aliases:c}})),Vt={};function St(n){return"string"==typeof n?Vt[n.toLowerCase()]||wt:n&&n.name?St(n.name):wt}Tt.forEach(n=>{Vt[n.name]=n,n.aliases.forEach(t=>{Vt[t]=n})});const Ct=t("Mode.mode","Mode.get",St);function Dt(){return Tt.slice()}var xt={get:St,names:function(){return Tt.map(n=>n.name)},all:Dt,entries:t("Mode.mode","Mode.all",Dt),mode:Ct};var Ot={fromRomanNumerals:function(n,t){return t.map(Pt).map(t=>w(n,v(t))+t.chordType)},toRomanNumerals:function(n,t){return t.map(t=>{const[e,o]=Nn(t);return Pt(v(T(n,e))).name+o})}};function _t(n){const t=F(n.map(qn));return n.length&&t.length===n.length?t.reduce((n,t)=>{const e=n[n.length-1];return n.concat(q(e,t).slice(1))},[t[0]]):[]}var kt={numeric:_t,chromatic:function(n,t){return _t(n).map(n=>Un(n,t))}};const $t={empty:!0,name:"",type:"",tonic:null,setNum:NaN,chroma:"",normalized:"",aliases:[],notes:[],intervals:[]};function qt(n){if("string"!=typeof n)return["",""];const t=n.indexOf(" "),e=d(n.substring(0,t));if(e.empty){const t=d(n);return t.empty?["",n]:[t.name,""]}const o=n.substring(e.name.length+1);return[e.name,o.length?o:""]}function Et(n){const t=Array.isArray(n)?n:qt(n),e=d(t[0]).name,o=hn(t[1]);if(o.empty)return $t;const r=o.name,m=e?o.intervals.map(n=>w(e,n)):[],a=e?e+" "+r:r;return{...o,name:a,type:r,tonic:e,notes:m}}var Ft,zt,Rt={get:Et,names:pn,extended:function(n){const t=Z(Et(n).chroma);return bn().filter(n=>t(n.chroma)).map(n=>n.name)},modeNames:function(n){const t=Et(n);if(t.empty)return[];const e=t.tonic?t.notes:t.intervals;return X(t.chroma).map((n,t)=>{const o=Et(n).name;return o?[e[t],o]:["",""]}).filter(n=>n[0])},reduced:function(n){const t=Y(Et(n).chroma);return bn().filter(n=>t(n.chroma)).map(n=>n.name)},scaleChords:function(n){const t=Y(Et(n).chroma);return cn().filter(n=>t(n.chroma)).map(n=>n.aliases[0])},scaleNotes:function(n){const t=n.map(n=>d(n).pc).filter(n=>n),e=t[0],o=rt(t);return E(o.indexOf(e),o)},tokenize:qt,scale:t("Scale.scale","Scale.get",Et)},Ut=(function(n,t){!function(n,t,e,o,r,m,a,i,c,s,u,P,l,M,d,f,p,h){t=t&&t.hasOwnProperty("default")?t.default:t,o=o&&o.hasOwnProperty("default")?o.default:o,r=r&&r.hasOwnProperty("default")?r.default:r,m=m&&m.hasOwnProperty("default")?m.default:m,i=i&&i.hasOwnProperty("default")?i.default:i,c=c&&c.hasOwnProperty("default")?c.default:c,s=s&&s.hasOwnProperty("default")?s.default:s,u=u&&u.hasOwnProperty("default")?u.default:u,P=P&&P.hasOwnProperty("default")?P.default:P,l=l&&l.hasOwnProperty("default")?l.default:l,M=M&&M.hasOwnProperty("default")?M.default:M,d=d&&d.hasOwnProperty("default")?d.default:d,f=f&&f.hasOwnProperty("default")?f.default:f,p=p&&p.hasOwnProperty("default")?p.default:p,h=h&&h.hasOwnProperty("default")?h.default:h;var y=a,b=l,g=r,A=h;Object.keys(a).forEach((function(t){"default"!==t&&Object.defineProperty(n,t,{enumerable:!0,get:function(){return a[t]}})})),n.AbcNotation=t,n.Array=e,n.Chord=o,n.ChordType=r,n.Collection=m,n.Core=a,n.Interval=i,n.Key=c,n.Midi=s,n.Mode=u,n.Note=P,n.Pcset=l,n.Progression=M,n.Range=d,n.RomanNumeral=f,n.Scale=p,n.ScaleType=h,n.ChordDictionary=g,n.PcSet=b,n.ScaleDictionary=A,n.Tonal=y,Object.defineProperty(n,"__esModule",{value:!0})}(t,_,$,Tn,Pn,z,V,_n,Nt,Bn,xt,ct,tn,Ot,kt,ht,Rt,In)}(Ft={exports:{}},Ft.exports),Ft.exports);return(zt=Ut)&&zt.__esModule&&Object.prototype.hasOwnProperty.call(zt,"default")?zt.default:zt}(); //# sourceMappingURL=tonal.min.js.map diff --git a/packages/tonal/browser/tonal.min.js.map b/packages/tonal/browser/tonal.min.js.map index 862a7121..3c06b434 100644 --- a/packages/tonal/browser/tonal.min.js.map +++ b/packages/tonal/browser/tonal.min.js.map @@ -1 +1 @@ -{"version":3,"file":"tonal.min.js","sources":["../../core/dist/index.esnext.js","../../abc-notation/dist/index.esnext.js","../../array/dist/index.esnext.js","../../pcset/dist/index.esnext.js","../../chord-dictionary/dist/index.esnext.js","../../scale-dictionary/dist/index.esnext.js","../../chord/dist/index.esnext.js","../../interval/dist/index.esnext.js","../../midi/dist/index.esnext.js","../../note/dist/index.esnext.js","../../roman-numeral/dist/index.esnext.js","../../key/dist/index.esnext.js","../../mode/dist/index.esnext.js","../../progression/dist/index.esnext.js","../../range/dist/index.esnext.js","../../scale/dist/index.esnext.js","../dist/index.es5.js"],"sourcesContent":["function isNamed(src) {\r\n return typeof src === \"object\" && typeof src.name === \"string\";\r\n}\n\nfunction isPitch(pitch) {\r\n return (typeof pitch === \"object\" &&\r\n typeof pitch.step === \"number\" &&\r\n typeof pitch.alt === \"number\");\r\n}\r\n// The nuuber of fifths of [C, D, E, F, G, A, B]\r\nconst FIFTHS = [0, 2, 4, -1, 1, 3, 5];\r\n// The number of octaves it span each step\r\nconst STEPS_TO_OCTS = FIFTHS.map((fifths) => Math.floor((fifths * 7) / 12));\r\nfunction encode(pitch) {\r\n const { step, alt, oct, dir = 1 } = pitch;\r\n const f = FIFTHS[step] + 7 * alt;\r\n if (oct === undefined) {\r\n return [dir * f];\r\n }\r\n const o = oct - STEPS_TO_OCTS[step] - 4 * alt;\r\n return [dir * f, dir * o];\r\n}\r\n// We need to get the steps from fifths\r\n// Fifths for CDEFGAB are [ 0, 2, 4, -1, 1, 3, 5 ]\r\n// We add 1 to fifths to avoid negative numbers, so:\r\n// for [\"F\", \"C\", \"G\", \"D\", \"A\", \"E\", \"B\"] we have:\r\nconst FIFTHS_TO_STEPS = [3, 0, 4, 1, 5, 2, 6];\r\nfunction decode(coord) {\r\n const [f, o, dir] = coord;\r\n const step = FIFTHS_TO_STEPS[unaltered(f)];\r\n const alt = Math.floor((f + 1) / 7);\r\n if (o === undefined) {\r\n return { step, alt, dir };\r\n }\r\n const oct = o + 4 * alt + STEPS_TO_OCTS[step];\r\n return { step, alt, oct, dir };\r\n}\r\n// Return the number of fifths as if it were unaltered\r\nfunction unaltered(f) {\r\n const i = (f + 1) % 7;\r\n return i < 0 ? 7 + i : i;\r\n}\n\nconst NoNote = { empty: true, name: \"\", pc: \"\", acc: \"\" };\r\nconst cache = {};\r\nconst fillStr = (s, n) => Array(n + 1).join(s);\r\nconst stepToLetter = (step) => \"CDEFGAB\".charAt(step);\r\nconst altToAcc = (alt) => alt < 0 ? fillStr(\"b\", -alt) : fillStr(\"#\", alt);\r\nconst accToAlt = (acc) => acc[0] === \"b\" ? -acc.length : acc.length;\r\n/**\r\n * Given a note literal (a note name or a note object), returns the Note object\r\n * @example\r\n * note('Bb4') // => { name: \"Bb4\", midi: 70, chroma: 10, ... }\r\n */\r\nfunction note(src) {\r\n return typeof src === \"string\"\r\n ? cache[src] || (cache[src] = parse(src))\r\n : isPitch(src)\r\n ? note(pitchName(src))\r\n : isNamed(src)\r\n ? note(src.name)\r\n : NoNote;\r\n}\r\nconst REGEX = /^([a-gA-G]?)(#{1,}|b{1,}|x{1,}|)(-?\\d*)\\s*(.*)$/;\r\n/**\r\n * @private\r\n */\r\nfunction tokenize(str) {\r\n const m = REGEX.exec(str);\r\n return [m[1].toUpperCase(), m[2].replace(/x/g, \"##\"), m[3], m[4]];\r\n}\r\n/**\r\n * @private\r\n */\r\nfunction coordToNote(noteCoord) {\r\n return note(decode(noteCoord));\r\n}\r\nconst SEMI = [0, 2, 4, 5, 7, 9, 11];\r\nfunction parse(noteName) {\r\n const tokens = tokenize(noteName);\r\n if (tokens[0] === \"\" || tokens[3] !== \"\") {\r\n return NoNote;\r\n }\r\n const letter = tokens[0];\r\n const acc = tokens[1];\r\n const octStr = tokens[2];\r\n const step = (letter.charCodeAt(0) + 3) % 7;\r\n const alt = accToAlt(acc);\r\n const oct = octStr.length ? +octStr : undefined;\r\n const coord = encode({ step, alt, oct });\r\n const name = letter + acc + octStr;\r\n const pc = letter + acc;\r\n const chroma = (SEMI[step] + alt + 120) % 12;\r\n const o = oct === undefined ? -100 : oct;\r\n const height = SEMI[step] + alt + 12 * (o + 1);\r\n const midi = height >= 0 && height <= 127 ? height : null;\r\n const freq = oct === undefined ? null : Math.pow(2, (height - 69) / 12) * 440;\r\n return {\r\n empty: false,\r\n acc,\r\n alt,\r\n chroma,\r\n coord,\r\n freq,\r\n height,\r\n letter,\r\n midi,\r\n name,\r\n oct,\r\n pc,\r\n step\r\n };\r\n}\r\nfunction pitchName(props) {\r\n const { step, alt, oct } = props;\r\n const letter = stepToLetter(step);\r\n if (!letter) {\r\n return \"\";\r\n }\r\n const pc = letter + altToAcc(alt);\r\n return oct || oct === 0 ? pc + oct : pc;\r\n}\n\nconst NoInterval = { empty: true, name: \"\", acc: \"\" };\r\n// shorthand tonal notation (with quality after number)\r\nconst INTERVAL_TONAL_REGEX = \"([-+]?\\\\d+)(d{1,4}|m|M|P|A{1,4})\";\r\n// standard shorthand notation (with quality before number)\r\nconst INTERVAL_SHORTHAND_REGEX = \"(AA|A|P|M|m|d|dd)([-+]?\\\\d+)\";\r\nconst REGEX$1 = new RegExp(\"^\" + INTERVAL_TONAL_REGEX + \"|\" + INTERVAL_SHORTHAND_REGEX + \"$\");\r\n/**\r\n * @private\r\n */\r\nfunction tokenize$1(str) {\r\n const m = REGEX$1.exec(`${str}`);\r\n if (m === null) {\r\n return [\"\", \"\"];\r\n }\r\n return m[1] ? [m[1], m[2]] : [m[4], m[3]];\r\n}\r\nconst cache$1 = {};\r\n/**\r\n * Get interval properties. It returns an object with:\r\n *\r\n * - name: the interval name\r\n * - num: the interval number\r\n * - type: 'perfectable' or 'majorable'\r\n * - q: the interval quality (d, m, M, A)\r\n * - dir: interval direction (1 ascending, -1 descending)\r\n * - simple: the simplified number\r\n * - semitones: the size in semitones\r\n * - chroma: the interval chroma\r\n *\r\n * @param {string} interval - the interval name\r\n * @return {Object} the interval properties\r\n *\r\n * @example\r\n * import { interval } from '@tonaljs/core'\r\n * interval('P5').semitones // => 7\r\n * interval('m3').type // => 'majorable'\r\n */\r\nfunction interval(src) {\r\n return typeof src === \"string\"\r\n ? cache$1[src] || (cache$1[src] = parse$1(src))\r\n : isPitch(src)\r\n ? interval(pitchName$1(src))\r\n : isNamed(src)\r\n ? interval(src.name)\r\n : NoInterval;\r\n}\r\nconst SIZES = [0, 2, 4, 5, 7, 9, 11];\r\nconst TYPES = \"PMMPPMM\";\r\nfunction parse$1(str) {\r\n const tokens = tokenize$1(str);\r\n if (tokens[0] === \"\") {\r\n return NoInterval;\r\n }\r\n const num = +tokens[0];\r\n const q = tokens[1];\r\n const step = (Math.abs(num) - 1) % 7;\r\n const t = TYPES[step];\r\n if (t === \"M\" && q === \"P\") {\r\n return NoInterval;\r\n }\r\n const type = t === \"M\" ? \"majorable\" : \"perfectable\";\r\n const name = \"\" + num + q;\r\n const dir = num < 0 ? -1 : 1;\r\n const simple = num === 8 || num === -8 ? num : dir * (step + 1);\r\n const alt = qToAlt(type, q);\r\n const oct = Math.floor((Math.abs(num) - 1) / 7);\r\n const semitones = dir * (SIZES[step] + alt + 12 * oct);\r\n const chroma = (((dir * (SIZES[step] + alt)) % 12) + 12) % 12;\r\n const coord = encode({ step, alt, oct, dir });\r\n return {\r\n empty: false,\r\n name,\r\n num,\r\n q,\r\n step,\r\n alt,\r\n dir,\r\n type,\r\n simple,\r\n semitones,\r\n chroma,\r\n coord,\r\n oct\r\n };\r\n}\r\n/**\r\n * @private\r\n */\r\nfunction coordToInterval(coord) {\r\n const [f, o = 0] = coord;\r\n const isDescending = f * 7 + o * 12 < 0;\r\n const ivl = isDescending ? [-f, -o, -1] : [f, o, 1];\r\n return interval(decode(ivl));\r\n}\r\nfunction qToAlt(type, q) {\r\n return (q === \"M\" && type === \"majorable\") ||\r\n (q === \"P\" && type === \"perfectable\")\r\n ? 0\r\n : q === \"m\" && type === \"majorable\"\r\n ? -1\r\n : /^A+$/.test(q)\r\n ? q.length\r\n : /^d+$/.test(q)\r\n ? -1 * (type === \"perfectable\" ? q.length : q.length + 1)\r\n : 0;\r\n}\r\n// return the interval name of a pitch\r\nfunction pitchName$1(props) {\r\n const { step, alt, oct = 0, dir } = props;\r\n if (!dir) {\r\n return \"\";\r\n }\r\n const num = step + 1 + 7 * oct;\r\n const d = dir < 0 ? \"-\" : \"\";\r\n const type = TYPES[step] === \"M\" ? \"majorable\" : \"perfectable\";\r\n const name = d + num + altToQ(type, alt);\r\n return name;\r\n}\r\nconst fillStr$1 = (s, n) => Array(Math.abs(n) + 1).join(s);\r\nfunction altToQ(type, alt) {\r\n if (alt === 0) {\r\n return type === \"majorable\" ? \"M\" : \"P\";\r\n }\r\n else if (alt === -1 && type === \"majorable\") {\r\n return \"m\";\r\n }\r\n else if (alt > 0) {\r\n return fillStr$1(\"A\", alt);\r\n }\r\n else {\r\n return fillStr$1(\"d\", type === \"perfectable\" ? alt : alt + 1);\r\n }\r\n}\n\n/**\r\n * Transpose a note by an interval.\r\n *\r\n * @param {string} note - the note or note name\r\n * @param {string} interval - the interval or interval name\r\n * @return {string} the transposed note name or empty string if not valid notes\r\n * @example\r\n * import { tranpose } from \"@tonaljs/core\"\r\n * transpose(\"d3\", \"3M\") // => \"F#3\"\r\n * transpose(\"D\", \"3M\") // => \"F#\"\r\n * [\"C\", \"D\", \"E\", \"F\", \"G\"].map(pc => transpose(pc, \"M3)) // => [\"E\", \"F#\", \"G#\", \"A\", \"B\"]\r\n */\r\nfunction transpose(noteName, intervalName) {\r\n const note$1 = note(noteName);\r\n const interval$1 = interval(intervalName);\r\n if (note$1.empty || interval$1.empty) {\r\n return \"\";\r\n }\r\n const noteCoord = note$1.coord;\r\n const intervalCoord = interval$1.coord;\r\n const tr = noteCoord.length === 1\r\n ? [noteCoord[0] + intervalCoord[0]]\r\n : [noteCoord[0] + intervalCoord[0], noteCoord[1] + intervalCoord[1]];\r\n return coordToNote(tr).name;\r\n}\r\n/**\r\n * Find the interval distance between two notes or coord classes.\r\n *\r\n * To find distance between coord classes, both notes must be coord classes and\r\n * the interval is always ascending\r\n *\r\n * @param {Note|string} from - the note or note name to calculate distance from\r\n * @param {Note|string} to - the note or note name to calculate distance to\r\n * @return {string} the interval name or empty string if not valid notes\r\n *\r\n */\r\nfunction distance(fromNote, toNote) {\r\n const from = note(fromNote);\r\n const to = note(toNote);\r\n if (from.empty || to.empty) {\r\n return \"\";\r\n }\r\n const fcoord = from.coord;\r\n const tcoord = to.coord;\r\n const fifths = tcoord[0] - fcoord[0];\r\n const octs = fcoord.length === 2 && tcoord.length === 2\r\n ? tcoord[1] - fcoord[1]\r\n : -Math.floor((fifths * 7) / 12);\r\n return coordToInterval([fifths, octs]).name;\r\n}\n\nexport { accToAlt, altToAcc, coordToInterval, coordToNote, decode, distance, encode, interval, isNamed, isPitch, note, tokenize$1 as tokenizeInterval, tokenize as tokenizeNote, transpose };\n//# sourceMappingURL=index.esnext.js.map\n","import { note, transpose as transpose$1 } from '@tonaljs/core';\n\nconst fillStr = (character, times) => Array(times + 1).join(character);\r\nconst REGEX = /^(_{1,}|=|\\^{1,}|)([abcdefgABCDEFG])([,']*)$/;\r\nfunction tokenize(str) {\r\n const m = REGEX.exec(str);\r\n if (!m) {\r\n return [\"\", \"\", \"\"];\r\n }\r\n return [m[1], m[2], m[3]];\r\n}\r\n/**\r\n * Convert a (string) note in ABC notation into a (string) note in scientific notation\r\n *\r\n * @example\r\n * abcToScientificNotation(\"c\") // => \"C5\"\r\n */\r\nfunction abcToScientificNotation(str) {\r\n const [acc, letter, oct] = tokenize(str);\r\n if (letter === \"\") {\r\n return \"\";\r\n }\r\n let o = 4;\r\n for (let i = 0; i < oct.length; i++) {\r\n o += oct.charAt(i) === \",\" ? -1 : 1;\r\n }\r\n const a = acc[0] === \"_\"\r\n ? acc.replace(/_/g, \"b\")\r\n : acc[0] === \"^\"\r\n ? acc.replace(/\\^/g, \"#\")\r\n : \"\";\r\n return letter.charCodeAt(0) > 96\r\n ? letter.toUpperCase() + a + (o + 1)\r\n : letter + a + o;\r\n}\r\n/**\r\n * Convert a (string) note in scientific notation into a (string) note in ABC notation\r\n *\r\n * @example\r\n * scientificToAbcNotation(\"C#4\") // => \"^C\"\r\n */\r\nfunction scientificToAbcNotation(str) {\r\n const n = note(str);\r\n if (n.empty || !n.oct) {\r\n return \"\";\r\n }\r\n const { letter, acc, oct } = n;\r\n const a = acc[0] === \"b\" ? acc.replace(/b/g, \"_\") : acc.replace(/#/g, \"^\");\r\n const l = oct > 4 ? letter.toLowerCase() : letter;\r\n const o = oct === 5 ? \"\" : oct > 4 ? fillStr(\"'\", oct - 5) : fillStr(\",\", 4 - oct);\r\n return a + l + o;\r\n}\r\nfunction transpose(note, interval) {\r\n return scientificToAbcNotation(transpose$1(abcToScientificNotation(note), interval));\r\n}\n\nexport { abcToScientificNotation, scientificToAbcNotation, tokenize, transpose };\n//# sourceMappingURL=index.esnext.js.map\n","import { note } from '@tonaljs/core';\n\n// ascending range\r\nfunction ascR(b, n) {\r\n const a = [];\r\n // tslint:disable-next-line:curly\r\n for (; n--; a[n] = n + b)\r\n ;\r\n return a;\r\n}\r\n// descending range\r\nfunction descR(b, n) {\r\n const a = [];\r\n // tslint:disable-next-line:curly\r\n for (; n--; a[n] = b - n)\r\n ;\r\n return a;\r\n}\r\n/**\r\n * Creates a numeric range\r\n *\r\n * @param {number} from\r\n * @param {number} to\r\n * @return {Array}\r\n *\r\n * @example\r\n * range(-2, 2) // => [-2, -1, 0, 1, 2]\r\n * range(2, -2) // => [2, 1, 0, -1, -2]\r\n */\r\nfunction range(from, to) {\r\n return from < to ? ascR(from, to - from + 1) : descR(from, from - to + 1);\r\n}\r\n/**\r\n * Rotates a list a number of times. It\"s completly agnostic about the\r\n * contents of the list.\r\n *\r\n * @param {Integer} times - the number of rotations\r\n * @param {Array} array\r\n * @return {Array} the rotated array\r\n *\r\n * @example\r\n * rotate(1, [1, 2, 3]) // => [2, 3, 1]\r\n */\r\nfunction rotate(times, arr) {\r\n const len = arr.length;\r\n const n = ((times % len) + len) % len;\r\n return arr.slice(n, len).concat(arr.slice(0, n));\r\n}\r\n/**\r\n * Return a copy of the array with the null values removed\r\n * @function\r\n * @param {Array} array\r\n * @return {Array}\r\n *\r\n * @example\r\n * compact([\"a\", \"b\", null, \"c\"]) // => [\"a\", \"b\", \"c\"]\r\n */\r\nfunction compact(arr) {\r\n return arr.filter(n => n === 0 || n);\r\n}\r\n/**\r\n * Sort an array of notes in ascending order. Pitch classes are listed\r\n * before notes. Any string that is not a note is removed.\r\n *\r\n * @param {string[]} notes\r\n * @return {string[]} sorted array of notes\r\n *\r\n * @example\r\n * sortedNoteNames(['c2', 'c5', 'c1', 'c0', 'c6', 'c'])\r\n * // => ['C', 'C0', 'C1', 'C2', 'C5', 'C6']\r\n * sortedNoteNames(['c', 'F', 'G', 'a', 'b', 'h', 'J'])\r\n * // => ['C', 'F', 'G', 'A', 'B']\r\n */\r\nfunction sortedNoteNames(notes) {\r\n const valid = notes.map(n => note(n)).filter(n => !n.empty);\r\n return valid.sort((a, b) => a.height - b.height).map(n => n.name);\r\n}\r\n/**\r\n * Get sorted notes with duplicates removed. Pitch classes are listed\r\n * before notes.\r\n *\r\n * @function\r\n * @param {string[]} array\r\n * @return {string[]} unique sorted notes\r\n *\r\n * @example\r\n * Array.sortedUniqNoteNames(['a', 'b', 'c2', '1p', 'p2', 'c2', 'b', 'c', 'c3' ])\r\n * // => [ 'C', 'A', 'B', 'C2', 'C3' ]\r\n */\r\nfunction sortedUniqNoteNames(arr) {\r\n return sortedNoteNames(arr).filter((n, i, a) => i === 0 || n !== a[i - 1]);\r\n}\r\n/**\r\n * Randomizes the order of the specified array in-place, using the Fisher–Yates shuffle.\r\n *\r\n * @function\r\n * @param {Array} array\r\n * @return {Array} the array shuffled\r\n *\r\n * @example\r\n * shuffle([\"C\", \"D\", \"E\", \"F\"]) // => [...]\r\n */\r\nfunction shuffle(arr, rnd = Math.random) {\r\n let i;\r\n let t;\r\n let m = arr.length;\r\n while (m) {\r\n i = Math.floor(rnd() * m--);\r\n t = arr[m];\r\n arr[m] = arr[i];\r\n arr[i] = t;\r\n }\r\n return arr;\r\n}\r\n/**\r\n * Get all permutations of an array\r\n *\r\n * @param {Array} array - the array\r\n * @return {Array} an array with all the permutations\r\n * @example\r\n * permutations([\"a\", \"b\", \"c\"])) // =>\r\n * [\r\n * [\"a\", \"b\", \"c\"],\r\n * [\"b\", \"a\", \"c\"],\r\n * [\"b\", \"c\", \"a\"],\r\n * [\"a\", \"c\", \"b\"],\r\n * [\"c\", \"a\", \"b\"],\r\n * [\"c\", \"b\", \"a\"]\r\n * ]\r\n */\r\nfunction permutations(arr) {\r\n if (arr.length === 0) {\r\n return [[]];\r\n }\r\n return permutations(arr.slice(1)).reduce((acc, perm) => {\r\n return acc.concat(arr.map((e, pos) => {\r\n const newPerm = perm.slice();\r\n newPerm.splice(pos, 0, arr[0]);\r\n return newPerm;\r\n }));\r\n }, []);\r\n}\n\nexport { compact, permutations, range, rotate, shuffle, sortedNoteNames, sortedUniqNoteNames };\n//# sourceMappingURL=index.esnext.js.map\n","import { range, compact, rotate } from '@tonaljs/array';\nimport { note, interval } from '@tonaljs/core';\n\nconst EmptyPcset = {\r\n empty: true,\r\n name: \"\",\r\n setNum: 0,\r\n chroma: \"000000000000\",\r\n normalized: \"000000000000\",\r\n intervals: []\r\n};\r\n// UTILITIES\r\nconst setNumToChroma = (num) => Number(num).toString(2);\r\nconst chromaToNumber = (chroma) => parseInt(chroma, 2);\r\nconst REGEX = /^[01]{12}$/;\r\nfunction isChroma(set) {\r\n return REGEX.test(set);\r\n}\r\nconst isPcsetNum = (set) => typeof set === \"number\" && set >= 0 && set <= 4095;\r\nconst isPcset = (set) => set && isChroma(set.chroma);\r\nconst cache = { [EmptyPcset.chroma]: EmptyPcset };\r\n/**\r\n * Get the pitch class set of a collection of notes or set number or chroma\r\n */\r\nfunction pcset(src) {\r\n const chroma = isChroma(src)\r\n ? src\r\n : isPcsetNum(src)\r\n ? setNumToChroma(src)\r\n : Array.isArray(src)\r\n ? listToChroma(src)\r\n : isPcset(src)\r\n ? src.chroma\r\n : EmptyPcset.chroma;\r\n return (cache[chroma] = cache[chroma] || chromaToPcset(chroma));\r\n}\r\nconst IVLS = \"1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M\".split(\" \");\r\n/**\r\n * @private\r\n * Get the intervals of a pcset *starting from C*\r\n * @param {Set} set - the pitch class set\r\n * @return {IntervalName[]} an array of interval names or an empty array\r\n * if not a valid pitch class set\r\n */\r\nfunction chromaToIntervals(chroma) {\r\n const intervals = [];\r\n for (let i = 0; i < 12; i++) {\r\n // tslint:disable-next-line:curly\r\n if (chroma.charAt(i) === \"1\")\r\n intervals.push(IVLS[i]);\r\n }\r\n return intervals;\r\n}\r\nlet all;\r\n/**\r\n * Get a list of all possible pitch class sets (all possible chromas) *having\r\n * C as root*. There are 2048 different chromas. If you want them with another\r\n * note you have to transpose it\r\n *\r\n * @see http://allthescales.org/\r\n * @return {Array} an array of possible chromas from '10000000000' to '11111111111'\r\n */\r\nfunction chromas() {\r\n all = all || range(2048, 4095).map(setNumToChroma);\r\n return all.slice();\r\n}\r\n/**\r\n * Given a a list of notes or a pcset chroma, produce the rotations\r\n * of the chroma discarding the ones that starts with \"0\"\r\n *\r\n * This is used, for example, to get all the modes of a scale.\r\n *\r\n * @param {Array|string} set - the list of notes or pitchChr of the set\r\n * @param {boolean} normalize - (Optional, true by default) remove all\r\n * the rotations that starts with \"0\"\r\n * @return {Array} an array with all the modes of the chroma\r\n *\r\n * @example\r\n * Pcset.modes([\"C\", \"D\", \"E\"]).map(Pcset.intervals)\r\n */\r\nfunction modes(set, normalize = true) {\r\n const pcs = pcset(set);\r\n const binary = pcs.chroma.split(\"\");\r\n return compact(binary.map((_, i) => {\r\n const r = rotate(i, binary);\r\n return normalize && r[0] === \"0\" ? null : r.join(\"\");\r\n }));\r\n}\r\n/**\r\n * Test if two pitch class sets are numentical\r\n *\r\n * @param {Array|string} set1 - one of the pitch class sets\r\n * @param {Array|string} set2 - the other pitch class set\r\n * @return {boolean} true if they are equal\r\n * @example\r\n * Pcset.isEqual([\"c2\", \"d3\"], [\"c5\", \"d2\"]) // => true\r\n */\r\nfunction isEqual(s1, s2) {\r\n return pcset(s1).setNum === pcset(s2).setNum;\r\n}\r\n/**\r\n * Create a function that test if a collection of notes is a\r\n * subset of a given set\r\n *\r\n * The function is curryfied.\r\n *\r\n * @param {PcsetChroma|NoteName[]} set - the superset to test against (chroma or\r\n * list of notes)\r\n * @return{function(PcsetChroma|NoteNames[]): boolean} a function accepting a set\r\n * to test against (chroma or list of notes)\r\n * @example\r\n * const inCMajor = Pcset.isSubsetOf([\"C\", \"E\", \"G\"])\r\n * inCMajor([\"e6\", \"c4\"]) // => true\r\n * inCMajor([\"e6\", \"c4\", \"d3\"]) // => false\r\n */\r\nfunction isSubsetOf(set) {\r\n const s = pcset(set).setNum;\r\n return (notes) => {\r\n const o = pcset(notes).setNum;\r\n // tslint:disable-next-line: no-bitwise\r\n return s && s !== o && (o & s) === o;\r\n };\r\n}\r\n/**\r\n * Create a function that test if a collection of notes is a\r\n * superset of a given set (it contains all notes and at least one more)\r\n *\r\n * @param {Set} set - an array of notes or a chroma set string to test against\r\n * @return {(subset: Set): boolean} a function that given a set\r\n * returns true if is a subset of the first one\r\n * @example\r\n * const extendsCMajor = Pcset.isSupersetOf([\"C\", \"E\", \"G\"])\r\n * extendsCMajor([\"e6\", \"a\", \"c4\", \"g2\"]) // => true\r\n * extendsCMajor([\"c6\", \"e4\", \"g3\"]) // => false\r\n */\r\nfunction isSupersetOf(set) {\r\n const s = pcset(set).setNum;\r\n return (notes) => {\r\n const o = pcset(notes).setNum;\r\n // tslint:disable-next-line: no-bitwise\r\n return s && s !== o && (o | s) === o;\r\n };\r\n}\r\n/**\r\n * Test if a given pitch class set includes a note\r\n *\r\n * @param {Array} set - the base set to test against\r\n * @param {string} note - the note to test\r\n * @return {boolean} true if the note is included in the pcset\r\n *\r\n * Can be partially applied\r\n *\r\n * @example\r\n * const isNoteInCMajor = isNoteIncludedInSet(['C', 'E', 'G'])\r\n * isNoteInCMajor('C4') // => true\r\n * isNoteInCMajor('C#4') // => false\r\n */\r\nfunction isNoteIncludedInSet(set) {\r\n const s = pcset(set);\r\n return (noteName) => {\r\n const n = note(noteName);\r\n return s && !n.empty && s.chroma.charAt(n.chroma) === \"1\";\r\n };\r\n}\r\n/** @deprecated use: isNoteIncludedIn */\r\nconst includes = isNoteIncludedInSet;\r\n/**\r\n * Filter a list with a pitch class set\r\n *\r\n * @param {Array|string} set - the pitch class set notes\r\n * @param {Array|string} notes - the note list to be filtered\r\n * @return {Array} the filtered notes\r\n *\r\n * @example\r\n * Pcset.filter([\"C\", \"D\", \"E\"], [\"c2\", \"c#2\", \"d2\", \"c3\", \"c#3\", \"d3\"]) // => [ \"c2\", \"d2\", \"c3\", \"d3\" ])\r\n * Pcset.filter([\"C2\"], [\"c2\", \"c#2\", \"d2\", \"c3\", \"c#3\", \"d3\"]) // => [ \"c2\", \"c3\" ])\r\n */\r\nfunction filter(set) {\r\n const isIncluded = isNoteIncludedInSet(set);\r\n return (notes) => {\r\n return notes.filter(isIncluded);\r\n };\r\n}\r\n// PRIVATE //\r\nfunction chromaRotations(chroma) {\r\n const binary = chroma.split(\"\");\r\n return binary.map((_, i) => rotate(i, binary).join(\"\"));\r\n}\r\nfunction chromaToPcset(chroma) {\r\n const setNum = chromaToNumber(chroma);\r\n const normalizedNum = chromaRotations(chroma)\r\n .map(chromaToNumber)\r\n .filter(n => n >= 2048)\r\n .sort()[0];\r\n const normalized = setNumToChroma(normalizedNum);\r\n const intervals = chromaToIntervals(chroma);\r\n return {\r\n empty: false,\r\n name: \"\",\r\n setNum,\r\n chroma,\r\n normalized,\r\n intervals\r\n };\r\n}\r\nfunction listToChroma(set) {\r\n if (set.length === 0) {\r\n return EmptyPcset.chroma;\r\n }\r\n let pitch;\r\n const binary = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\r\n // tslint:disable-next-line:prefer-for-of\r\n for (let i = 0; i < set.length; i++) {\r\n pitch = note(set[i]);\r\n // tslint:disable-next-line: curly\r\n if (pitch.empty)\r\n pitch = interval(set[i]);\r\n // tslint:disable-next-line: curly\r\n if (!pitch.empty)\r\n binary[pitch.chroma] = 1;\r\n }\r\n return binary.join(\"\");\r\n}\n\nexport { EmptyPcset, chromaToIntervals, chromas, filter, includes, isEqual, isNoteIncludedInSet, isSubsetOf, isSupersetOf, modes, pcset };\n//# sourceMappingURL=index.esnext.js.map\n","import { pcset, EmptyPcset } from '@tonaljs/pcset';\n\n/**\r\n * @private\r\n * Chord List\r\n * Source: https://en.wikibooks.org/wiki/Music_Theory/Complete_List_of_Chord_Patterns\r\n * Format: [\"intervals\", \"full name\", \"abrv1 abrv2\"]\r\n */\r\nconst CHORDS = [\r\n // ==Major==\r\n [\"1P 3M 5P\", \"major\", \"M \"],\r\n [\"1P 3M 5P 7M\", \"major seventh\", \"maj7 Δ ma7 M7 Maj7\"],\r\n [\"1P 3M 5P 7M 9M\", \"major ninth\", \"maj9 Δ9\"],\r\n [\"1P 3M 5P 7M 9M 13M\", \"major thirteenth\", \"maj13 Maj13\"],\r\n [\"1P 3M 5P 6M\", \"sixth\", \"6 add6 add13 M6\"],\r\n [\"1P 3M 5P 6M 9M\", \"sixth/ninth\", \"6/9 69\"],\r\n [\"1P 3M 5P 7M 11A\", \"lydian\", \"maj#4 Δ#4 Δ#11\"],\r\n [\"1P 3M 6m 7M\", \"major seventh b6\", \"M7b6\"],\r\n // ==Minor==\r\n // '''Normal'''\r\n [\"1P 3m 5P\", \"minor\", \"m min -\"],\r\n [\"1P 3m 5P 7m\", \"minor seventh\", \"m7 min7 mi7 -7\"],\r\n [\"1P 3m 5P 7M\", \"minor/major seventh\", \"m/ma7 m/maj7 mM7 m/M7 -Δ7 mΔ\"],\r\n [\"1P 3m 5P 6M\", \"minor sixth\", \"m6\"],\r\n [\"1P 3m 5P 7m 9M\", \"minor ninth\", \"m9\"],\r\n [\"1P 3m 5P 7m 9M 11P\", \"minor eleventh\", \"m11\"],\r\n [\"1P 3m 5P 7m 9M 13M\", \"minor thirteenth\", \"m13\"],\r\n // '''Diminished'''\r\n [\"1P 3m 5d\", \"diminished\", \"dim ° o\"],\r\n [\"1P 3m 5d 7d\", \"diminished seventh\", \"dim7 °7 o7\"],\r\n [\"1P 3m 5d 7m\", \"half-diminished\", \"m7b5 ø\"],\r\n // ==Dominant/Seventh==\r\n // '''Normal'''\r\n [\"1P 3M 5P 7m\", \"dominant seventh\", \"7 dom\"],\r\n [\"1P 3M 5P 7m 9M\", \"dominant ninth\", \"9\"],\r\n [\"1P 3M 5P 7m 9M 13M\", \"dominant thirteenth\", \"13\"],\r\n [\"1P 3M 5P 7m 11A\", \"lydian dominant seventh\", \"7#11 7#4\"],\r\n // '''Altered'''\r\n [\"1P 3M 5P 7m 9m\", \"dominant b9\", \"7b9\"],\r\n [\"1P 3M 5P 7m 9A\", \"dominant #9\", \"7#9\"],\r\n [\"1P 3M 7m 9m\", \"altered\", \"alt7\"],\r\n // '''Suspended'''\r\n [\"1P 4P 5P\", \"suspended 4th\", \"sus4\"],\r\n [\"1P 2M 5P\", \"suspended 2nd\", \"sus2\"],\r\n [\"1P 4P 5P 7m\", \"suspended 4th seventh\", \"7sus4\"],\r\n [\"1P 5P 7m 9M 11P\", \"eleventh\", \"11\"],\r\n [\"1P 4P 5P 7m 9m\", \"suspended 4th b9\", \"b9sus phryg\"],\r\n // ==Other==\r\n [\"1P 5P\", \"fifth\", \"5\"],\r\n [\"1P 3M 5A\", \"augmented\", \"aug + +5\"],\r\n [\"1P 3M 5A 7M\", \"augmented seventh\", \"maj7#5 maj7+5\"],\r\n [\"1P 3M 5P 7M 9M 11A\", \"major #11 (lydian)\", \"maj9#11 Δ9#11\"],\r\n // ==Legacy==\r\n [\"1P 2M 4P 5P\", \"\", \"sus24 sus4add9\"],\r\n [\"1P 3M 13m\", \"\", \"Mb6\"],\r\n [\"1P 3M 5A 7M 9M\", \"\", \"maj9#5 Maj9#5\"],\r\n [\"1P 3M 5A 7m\", \"\", \"7#5 +7 7aug aug7\"],\r\n [\"1P 3M 5A 7m 9A\", \"\", \"7#5#9 7alt 7#5#9_ 7#9b13_\"],\r\n [\"1P 3M 5A 7m 9M\", \"\", \"9#5 9+\"],\r\n [\"1P 3M 5A 7m 9M 11A\", \"\", \"9#5#11\"],\r\n [\"1P 3M 5A 7m 9m\", \"\", \"7#5b9\"],\r\n [\"1P 3M 5A 7m 9m 11A\", \"\", \"7#5b9#11\"],\r\n [\"1P 3M 5A 9A\", \"\", \"+add#9\"],\r\n [\"1P 3M 5A 9M\", \"\", \"M#5add9 +add9\"],\r\n [\"1P 3M 5P 6M 11A\", \"\", \"M6#11 M6b5 6#11 6b5\"],\r\n [\"1P 3M 5P 6M 7M 9M\", \"\", \"M7add13\"],\r\n [\"1P 3M 5P 6M 9M 11A\", \"\", \"69#11\"],\r\n [\"1P 3M 5P 6m 7m\", \"\", \"7b6\"],\r\n [\"1P 3M 5P 7M 9A 11A\", \"\", \"maj7#9#11\"],\r\n [\"1P 3M 5P 7M 9M 11A 13M\", \"\", \"M13#11 maj13#11 M13+4 M13#4\"],\r\n [\"1P 3M 5P 7M 9m\", \"\", \"M7b9\"],\r\n [\"1P 3M 5P 7m 11A 13m\", \"\", \"7#11b13 7b5b13\"],\r\n [\"1P 3M 5P 7m 13M\", \"\", \"7add6 67 7add13\"],\r\n [\"1P 3M 5P 7m 9A 11A\", \"\", \"7#9#11 7b5#9\"],\r\n [\"1P 3M 5P 7m 9A 11A 13M\", \"\", \"13#9#11\"],\r\n [\"1P 3M 5P 7m 9A 11A 13m\", \"\", \"7#9#11b13\"],\r\n [\"1P 3M 5P 7m 9A 13M\", \"\", \"13#9 13#9_\"],\r\n [\"1P 3M 5P 7m 9A 13m\", \"\", \"7#9b13\"],\r\n [\"1P 3M 5P 7m 9M 11A\", \"\", \"9#11 9+4 9#4 9#11_ 9#4_\"],\r\n [\"1P 3M 5P 7m 9M 11A 13M\", \"\", \"13#11 13+4 13#4\"],\r\n [\"1P 3M 5P 7m 9M 11A 13m\", \"\", \"9#11b13 9b5b13\"],\r\n [\"1P 3M 5P 7m 9m 11A\", \"\", \"7b9#11 7b5b9\"],\r\n [\"1P 3M 5P 7m 9m 11A 13M\", \"\", \"13b9#11\"],\r\n [\"1P 3M 5P 7m 9m 11A 13m\", \"\", \"7b9b13#11 7b9#11b13 7b5b9b13\"],\r\n [\"1P 3M 5P 7m 9m 13M\", \"\", \"13b9\"],\r\n [\"1P 3M 5P 7m 9m 13m\", \"\", \"7b9b13\"],\r\n [\"1P 3M 5P 7m 9m 9A\", \"\", \"7b9#9\"],\r\n [\"1P 3M 5P 9M\", \"\", \"Madd9 2 add9 add2\"],\r\n [\"1P 3M 5P 9m\", \"\", \"Maddb9\"],\r\n [\"1P 3M 5d\", \"\", \"Mb5\"],\r\n [\"1P 3M 5d 6M 7m 9M\", \"\", \"13b5\"],\r\n [\"1P 3M 5d 7M\", \"\", \"M7b5\"],\r\n [\"1P 3M 5d 7M 9M\", \"\", \"M9b5\"],\r\n [\"1P 3M 5d 7m\", \"\", \"7b5\"],\r\n [\"1P 3M 5d 7m 9M\", \"\", \"9b5\"],\r\n [\"1P 3M 7m\", \"\", \"7no5\"],\r\n [\"1P 3M 7m 13m\", \"\", \"7b13\"],\r\n [\"1P 3M 7m 9M\", \"\", \"9no5\"],\r\n [\"1P 3M 7m 9M 13M\", \"\", \"13no5\"],\r\n [\"1P 3M 7m 9M 13m\", \"\", \"9b13\"],\r\n [\"1P 3m 4P 5P\", \"\", \"madd4\"],\r\n [\"1P 3m 5A\", \"\", \"m#5 m+ mb6\"],\r\n [\"1P 3m 5P 6M 9M\", \"\", \"m69 _69\"],\r\n [\"1P 3m 5P 6m 7M\", \"\", \"mMaj7b6\"],\r\n [\"1P 3m 5P 6m 7M 9M\", \"\", \"mMaj9b6\"],\r\n [\"1P 3m 5P 7M 9M\", \"\", \"mMaj9 -Maj9\"],\r\n [\"1P 3m 5P 7m 11P\", \"\", \"m7add11 m7add4\"],\r\n [\"1P 3m 5P 9M\", \"\", \"madd9\"],\r\n [\"1P 3m 5d 6M 7M\", \"\", \"o7M7\"],\r\n [\"1P 3m 5d 7M\", \"\", \"oM7\"],\r\n [\"1P 3m 6m 7M\", \"\", \"mb6M7\"],\r\n [\"1P 3m 6m 7m\", \"\", \"m7#5\"],\r\n [\"1P 3m 6m 7m 9M\", \"\", \"m9#5\"],\r\n [\"1P 3m 6m 7m 9M 11P\", \"\", \"m11A\"],\r\n [\"1P 3m 6m 9m\", \"\", \"mb6b9\"],\r\n [\"1P 3m 7m 12d 2M\", \"\", \"m9b5 h9 -9b5\"],\r\n [\"1P 3m 7m 12d 2M 4P\", \"\", \"m11b5 h11 _11b5\"],\r\n [\"1P 4P 5A 7M\", \"\", \"M7#5sus4\"],\r\n [\"1P 4P 5A 7M 9M\", \"\", \"M9#5sus4\"],\r\n [\"1P 4P 5A 7m\", \"\", \"7#5sus4\"],\r\n [\"1P 4P 5P 7M\", \"\", \"M7sus4\"],\r\n [\"1P 4P 5P 7M 9M\", \"\", \"M9sus4\"],\r\n [\"1P 4P 5P 7m 9M\", \"\", \"9sus4 9sus\"],\r\n [\"1P 4P 5P 7m 9M 13M\", \"\", \"13sus4 13sus\"],\r\n [\"1P 4P 5P 7m 9m 13m\", \"\", \"7sus4b9b13 7b9b13sus4\"],\r\n [\"1P 4P 7m 10m\", \"\", \"4 quartal\"],\r\n [\"1P 5P 7m 9m 11P\", \"\", \"11b9\"]\r\n];\n\nconst NoChordType = {\r\n ...EmptyPcset,\r\n name: \"\",\r\n quality: \"Unknown\",\r\n intervals: [],\r\n aliases: []\r\n};\r\nlet chords = [];\r\nlet index = {};\r\n/**\r\n * Given a chord name or chroma, return the chord properties\r\n * @param {string} source - chord name or pitch class set chroma\r\n * @example\r\n * import { get } from 'tonaljs/chord-dictionary'\r\n * get('major') // => { name: 'major', ... }\r\n */\r\nfunction get(type) {\r\n return index[type] || NoChordType;\r\n}\r\n/**\r\n * @deprecated\r\n * @see get\r\n */\r\nfunction chordType(type) {\r\n // tslint:disable-next-line\r\n console.warn(\"ChordDictionary.chordType is deprecated. Use ChordDictionary.get instead\");\r\n return get(type);\r\n}\r\n/**\r\n * Keys used to reference chord types\r\n */\r\nfunction keys() {\r\n return Object.keys(index);\r\n}\r\n/**\r\n * Return a list of all chord types\r\n */\r\nfunction entries() {\r\n return chords.slice();\r\n}\r\n/**\r\n * Clear the dictionary\r\n */\r\nfunction clear() {\r\n chords = [];\r\n index = {};\r\n}\r\n/**\r\n * Add a chord to the dictionary.\r\n * @param intervals\r\n * @param aliases\r\n * @param [fullName]\r\n */\r\nfunction add(intervals, aliases, fullName) {\r\n const quality = getQuality(intervals);\r\n const chord = {\r\n ...pcset(intervals),\r\n name: fullName || \"\",\r\n quality,\r\n intervals,\r\n aliases\r\n };\r\n chords.push(chord);\r\n if (chord.name) {\r\n index[chord.name] = chord;\r\n }\r\n index[chord.setNum] = chord;\r\n index[chord.chroma] = chord;\r\n chord.aliases.forEach(alias => addAlias(chord, alias));\r\n}\r\nfunction addAlias(chord, alias) {\r\n index[alias] = chord;\r\n}\r\nfunction getQuality(intervals) {\r\n const has = (interval) => intervals.indexOf(interval) !== -1;\r\n return has(\"5A\")\r\n ? \"Augmented\"\r\n : has(\"3M\")\r\n ? \"Major\"\r\n : has(\"5d\")\r\n ? \"Diminished\"\r\n : has(\"3m\")\r\n ? \"Minor\"\r\n : \"Unknown\";\r\n}\r\nCHORDS.forEach(([ivls, fullName, names]) => add(ivls.split(\" \"), names.split(\" \"), fullName));\r\nchords.sort((a, b) => a.setNum - b.setNum);\n\nexport { add, addAlias, chordType, clear, entries, get, keys };\n//# sourceMappingURL=index.esnext.js.map\n","import { EmptyPcset, pcset } from '@tonaljs/pcset';\n\n// SCALES\r\n// Format: [\"intervals\", \"name\", \"alias1\", \"alias2\", ...]\r\nconst SCALES = [\r\n // 5-note scales\r\n [\"1P 2M 3M 5P 6M\", \"major pentatonic\", \"pentatonic\"],\r\n [\"1P 3M 4P 5P 7M\", \"ionian pentatonic\"],\r\n [\"1P 3M 4P 5P 7m\", \"mixolydian pentatonic\", \"indian\"],\r\n [\"1P 2M 4P 5P 6M\", \"ritusen\"],\r\n [\"1P 2M 4P 5P 7m\", \"egyptian\"],\r\n [\"1P 3M 4P 5d 7m\", \"neopolitan major pentatonic\"],\r\n [\"1P 3m 4P 5P 6m\", \"vietnamese 1\"],\r\n [\"1P 2m 3m 5P 6m\", \"pelog\"],\r\n [\"1P 2m 4P 5P 6m\", \"kumoijoshi\"],\r\n [\"1P 2M 3m 5P 6m\", \"hirajoshi\"],\r\n [\"1P 2m 4P 5d 7m\", \"iwato\"],\r\n [\"1P 2m 4P 5P 7m\", \"in-sen\"],\r\n [\"1P 3M 4A 5P 7M\", \"lydian pentatonic\", \"chinese\"],\r\n [\"1P 3m 4P 6m 7m\", \"malkos raga\"],\r\n [\"1P 3m 4P 5d 7m\", \"locrian pentatonic\", \"minor seven flat five pentatonic\"],\r\n [\"1P 3m 4P 5P 7m\", \"minor pentatonic\", \"vietnamese 2\"],\r\n [\"1P 3m 4P 5P 6M\", \"minor six pentatonic\"],\r\n [\"1P 2M 3m 5P 6M\", \"flat three pentatonic\", \"kumoi\"],\r\n [\"1P 2M 3M 5P 6m\", \"flat six pentatonic\"],\r\n [\"1P 2m 3M 5P 6M\", \"scriabin\"],\r\n [\"1P 3M 5d 6m 7m\", \"whole tone pentatonic\"],\r\n [\"1P 3M 4A 5A 7M\", \"lydian #5P pentatonic\"],\r\n [\"1P 3M 4A 5P 7m\", \"lydian dominant pentatonic\"],\r\n [\"1P 3m 4P 5P 7M\", \"minor #7M pentatonic\"],\r\n [\"1P 3m 4d 5d 7m\", \"super locrian pentatonic\"],\r\n // 6-note scales\r\n [\"1P 2M 3m 4P 5P 7M\", \"minor hexatonic\"],\r\n [\"1P 2A 3M 5P 5A 7M\", \"augmented\"],\r\n [\"1P 3m 4P 5d 5P 7m\", \"minor blues\", \"blues\"],\r\n [\"1P 2M 3m 3M 5P 6M\", \"major blues\"],\r\n [\"1P 2M 4P 5P 6M 7m\", \"piongio\"],\r\n [\"1P 2m 3M 4A 6M 7m\", \"prometheus neopolitan\"],\r\n [\"1P 2M 3M 4A 6M 7m\", \"prometheus\"],\r\n [\"1P 2m 3M 5d 6m 7m\", \"mystery #1\"],\r\n [\"1P 2m 3M 4P 5A 6M\", \"six tone symmetric\"],\r\n [\"1P 2M 3M 4A 5A 7m\", \"whole tone\"],\r\n // 7-note scales\r\n [\"1P 2M 3M 4P 5d 6m 7m\", \"locrian major\", \"arabian\"],\r\n [\"1P 2m 3M 4A 5P 6m 7M\", \"double harmonic lydian\"],\r\n [\"1P 2M 3m 4P 5P 6m 7M\", \"harmonic minor\"],\r\n [\r\n \"1P 2m 3m 3M 5d 6m 7m\",\r\n \"altered\",\r\n \"super locrian\",\r\n \"diminished whole tone\",\r\n \"pomeroy\"\r\n ],\r\n [\"1P 2M 3m 4P 5d 6m 7m\", \"locrian #2\", \"half-diminished\", '\"aeolian b5'],\r\n [\r\n \"1P 2M 3M 4P 5P 6m 7m\",\r\n \"mixolydian b6\",\r\n \"melodic minor fifth mode\",\r\n \"hindu\"\r\n ],\r\n [\"1P 2M 3M 4A 5P 6M 7m\", \"lydian dominant\", \"lydian b7\", \"overtone\"],\r\n [\"1P 2M 3M 4A 5P 6M 7M\", \"lydian\"],\r\n [\"1P 2M 3M 4A 5A 6M 7M\", \"lydian augmented\"],\r\n [\r\n \"1P 2m 3m 4P 5P 6M 7m\",\r\n \"dorian b2\",\r\n \"phrygian #6\",\r\n \"melodic minor second mode\"\r\n ],\r\n [\"1P 2M 3m 4P 5P 6M 7M\", \"melodic minor\"],\r\n [\"1P 2m 3m 4P 5d 6m 7m\", \"locrian\"],\r\n [\r\n \"1P 2m 3m 4d 5d 6m 7d\",\r\n \"ultralocrian\",\r\n \"superlocrian bb7\",\r\n \"·superlocrian diminished\"\r\n ],\r\n [\"1P 2m 3m 4P 5d 6M 7m\", \"locrian 6\", \"locrian natural 6\", \"locrian sharp 6\"],\r\n [\"1P 2A 3M 4P 5P 5A 7M\", \"augmented heptatonic\"],\r\n [\"1P 2M 3m 5d 5P 6M 7m\", \"romanian minor\"],\r\n [\"1P 2M 3m 4A 5P 6M 7m\", \"dorian #4\"],\r\n [\"1P 2M 3m 4A 5P 6M 7M\", \"lydian diminished\"],\r\n [\"1P 2m 3m 4P 5P 6m 7m\", \"phrygian\"],\r\n [\"1P 2M 3M 4A 5A 7m 7M\", \"leading whole tone\"],\r\n [\"1P 2M 3M 4A 5P 6m 7m\", \"lydian minor\"],\r\n [\"1P 2m 3M 4P 5P 6m 7m\", \"phrygian dominant\", \"spanish\", \"phrygian major\"],\r\n [\"1P 2m 3m 4P 5P 6m 7M\", \"balinese\"],\r\n [\"1P 2m 3m 4P 5P 6M 7M\", \"neopolitan major\"],\r\n [\"1P 2M 3m 4P 5P 6m 7m\", \"aeolian\", \"minor\"],\r\n [\"1P 2M 3M 4P 5P 6m 7M\", \"harmonic major\"],\r\n [\"1P 2m 3M 4P 5P 6m 7M\", \"double harmonic major\", \"gypsy\"],\r\n [\"1P 2M 3m 4P 5P 6M 7m\", \"dorian\"],\r\n [\"1P 2M 3m 4A 5P 6m 7M\", \"hungarian minor\"],\r\n [\"1P 2A 3M 4A 5P 6M 7m\", \"hungarian major\"],\r\n [\"1P 2m 3M 4P 5d 6M 7m\", \"oriental\"],\r\n [\"1P 2m 3m 3M 4A 5P 7m\", \"flamenco\"],\r\n [\"1P 2m 3m 4A 5P 6m 7M\", \"todi raga\"],\r\n [\"1P 2M 3M 4P 5P 6M 7m\", \"mixolydian\", \"dominant\"],\r\n [\"1P 2m 3M 4P 5d 6m 7M\", \"persian\"],\r\n [\"1P 2M 3M 4P 5P 6M 7M\", \"major\", \"ionian\"],\r\n [\"1P 2m 3M 5d 6m 7m 7M\", \"enigmatic\"],\r\n [\r\n \"1P 2M 3M 4P 5A 6M 7M\",\r\n \"major augmented\",\r\n \"major #5\",\r\n \"ionian augmented\",\r\n \"ionian #5\"\r\n ],\r\n [\"1P 2A 3M 4A 5P 6M 7M\", \"lydian #9\"],\r\n // 8-note scales\r\n [\"1P 2m 3M 4P 4A 5P 6m 7M\", \"purvi raga\"],\r\n [\"1P 2m 3m 3M 4P 5P 6m 7m\", \"spanish heptatonic\"],\r\n [\"1P 2M 3M 4P 5P 6M 7m 7M\", \"bebop\"],\r\n [\"1P 2M 3m 3M 4P 5P 6M 7m\", \"bebop minor\"],\r\n [\"1P 2M 3M 4P 5P 5A 6M 7M\", \"bebop major\"],\r\n [\"1P 2m 3m 4P 5d 5P 6m 7m\", \"bebop locrian\"],\r\n [\"1P 2M 3m 4P 5P 6m 7m 7M\", \"minor bebop\"],\r\n [\"1P 2M 3m 4P 5d 6m 6M 7M\", \"diminished\", \"whole-half diminished\"],\r\n [\"1P 2M 3M 4P 5d 5P 6M 7M\", \"ichikosucho\"],\r\n [\"1P 2M 3m 4P 5P 6m 6M 7M\", \"minor six diminished\"],\r\n [\"1P 2m 3m 3M 4A 5P 6M 7m\", \"half-whole diminished\", \"dominant diminished\"],\r\n [\"1P 3m 3M 4P 5P 6M 7m 7M\", \"kafi raga\"],\r\n // 9-note scales\r\n [\"1P 2M 3m 3M 4P 5d 5P 6M 7m\", \"composite blues\"],\r\n // 12-note scales\r\n [\"1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M\", \"chromatic\"]\r\n];\n\nconst NoScaleType = {\r\n ...EmptyPcset,\r\n intervals: [],\r\n aliases: []\r\n};\r\nlet scales = [];\r\nlet index = {};\r\n/**\r\n * Given a scale name or chroma, return the scale properties\r\n *\r\n * @param {string} type - scale name or pitch class set chroma\r\n * @example\r\n * import { get } from 'tonaljs/scale-dictionary'\r\n * get('major') // => { name: 'major', ... }\r\n */\r\nfunction get(type) {\r\n return index[type] || NoScaleType;\r\n}\r\n/**\r\n * Given a scale name or chroma, return the scale properties\r\n * @deprecated\r\n * @see Scale.get\r\n */\r\nfunction scaleType(type) {\r\n // tslint:disable-next-line\r\n console.warn(\"ScaleDictionary.scaleType is deprecated. Use ScaleDictionary.get instead\");\r\n return get(type);\r\n}\r\n/**\r\n * Return a list of all scale types\r\n */\r\nfunction entries() {\r\n return scales.slice();\r\n}\r\n/**\r\n * Keys used to reference scale types\r\n */\r\nfunction keys() {\r\n return Object.keys(index);\r\n}\r\n/**\r\n * Clear the dictionary\r\n */\r\nfunction clear() {\r\n scales = [];\r\n index = {};\r\n}\r\n/**\r\n * Add a scale into dictionary\r\n * @param intervals\r\n * @param name\r\n * @param aliases\r\n */\r\nfunction add(intervals, name, aliases = []) {\r\n const scale = { ...pcset(intervals), name, intervals, aliases };\r\n scales.push(scale);\r\n index[scale.name] = scale;\r\n index[scale.setNum] = scale;\r\n index[scale.chroma] = scale;\r\n scale.aliases.forEach(alias => addAlias(scale, alias));\r\n return scale;\r\n}\r\nfunction addAlias(scale, alias) {\r\n index[alias] = scale;\r\n}\r\nSCALES.forEach(([ivls, name, ...aliases]) => add(ivls.split(\" \"), name, aliases));\n\nexport { NoScaleType, add, addAlias, clear, entries, get, keys, scaleType };\n//# sourceMappingURL=index.esnext.js.map\n","import { get, entries as entries$1 } from '@tonaljs/chord-dictionary';\nimport { tokenizeNote, transpose as transpose$1, note } from '@tonaljs/core';\nimport { isSupersetOf, isSubsetOf } from '@tonaljs/pcset';\nimport { entries } from '@tonaljs/scale-dictionary';\n\nconst NoChord = {\r\n empty: true,\r\n name: \"\",\r\n type: \"\",\r\n tonic: null,\r\n setNum: NaN,\r\n quality: \"Unknown\",\r\n chroma: \"\",\r\n normalized: \"\",\r\n aliases: [],\r\n notes: [],\r\n intervals: []\r\n};\r\n// 6, 64, 7, 9, 11 and 13 are consider part of the chord\r\n// (see https://github.com/danigb/tonal/issues/55)\r\nconst NUM_TYPES = /^(6|64|7|9|11|13)$/;\r\n/**\r\n * Tokenize a chord name. It returns an array with the tonic and chord type\r\n * If not tonic is found, all the name is considered the chord name.\r\n *\r\n * This function does NOT check if the chord type exists or not. It only tries\r\n * to split the tonic and chord type.\r\n *\r\n * @function\r\n * @param {string} name - the chord name\r\n * @return {Array} an array with [tonic, type]\r\n * @example\r\n * tokenize(\"Cmaj7\") // => [ \"C\", \"maj7\" ]\r\n * tokenize(\"C7\") // => [ \"C\", \"7\" ]\r\n * tokenize(\"mMaj7\") // => [ null, \"mMaj7\" ]\r\n * tokenize(\"Cnonsense\") // => [ null, \"nonsense\" ]\r\n */\r\nfunction tokenize(name) {\r\n const [letter, acc, oct, type] = tokenizeNote(name);\r\n if (letter === \"\") {\r\n return [\"\", name];\r\n }\r\n // aug is augmented (see https://github.com/danigb/tonal/issues/55)\r\n if (letter === \"A\" && type === \"ug\") {\r\n return [\"\", \"aug\"];\r\n }\r\n // see: https://github.com/tonaljs/tonal/issues/70\r\n if (!type && (oct === \"4\" || oct === \"5\")) {\r\n return [letter + acc, oct];\r\n }\r\n if (NUM_TYPES.test(oct)) {\r\n return [letter + acc, oct + type];\r\n }\r\n else {\r\n return [letter + acc + oct, type];\r\n }\r\n}\r\n/**\r\n * Get a Chord from a chord name.\r\n */\r\nfunction chord(src) {\r\n const { type, tonic } = findChord(src);\r\n if (!type || type.empty) {\r\n return NoChord;\r\n }\r\n const notes = tonic\r\n ? type.intervals.map(i => transpose$1(tonic, i))\r\n : [];\r\n const name = tonic ? tonic + \" \" + type.name : type.name;\r\n return { ...type, name, type: type.name, tonic: tonic || \"\", notes };\r\n}\r\nfunction findChord(src) {\r\n if (!src || !src.length) {\r\n return {};\r\n }\r\n const tokens = Array.isArray(src) ? src : tokenize(src);\r\n const tonic = note(tokens[0]).name;\r\n const type = get(tokens[1]);\r\n if (!type.empty) {\r\n return { tonic, type };\r\n }\r\n else if (tonic && typeof src === \"string\") {\r\n return { tonic: \"\", type: get(src) };\r\n }\r\n else {\r\n return {};\r\n }\r\n}\r\n/**\r\n * Transpose a chord name\r\n *\r\n * @param {string} chordName - the chord name\r\n * @return {string} the transposed chord\r\n *\r\n * @example\r\n * transpose('Dm7', 'P4') // => 'Gm7\r\n */\r\nfunction transpose(chordName, interval) {\r\n const [tonic, type] = tokenize(chordName);\r\n if (!tonic) {\r\n return name;\r\n }\r\n return transpose$1(tonic, interval) + type;\r\n}\r\n/**\r\n * Get all scales where the given chord fits\r\n *\r\n * @example\r\n * chordScales('C7b9')\r\n * // => [\"phrygian dominant\", \"flamenco\", \"spanish heptatonic\", \"half-whole diminished\", \"chromatic\"]\r\n */\r\nfunction chordScales(name) {\r\n const s = chord(name);\r\n const isChordIncluded = isSupersetOf(s.chroma);\r\n return entries()\r\n .filter(scale => isChordIncluded(scale.chroma))\r\n .map(scale => scale.name);\r\n}\r\n/**\r\n * Get all chords names that are a superset of the given one\r\n * (has the same notes and at least one more)\r\n *\r\n * @function\r\n * @example\r\n * extended(\"CMaj7\")\r\n * // => [ 'Cmaj#4', 'Cmaj7#9#11', 'Cmaj9', 'CM7add13', 'Cmaj13', 'Cmaj9#11', 'CM13#11', 'CM7b9' ]\r\n */\r\nfunction extended(chordName) {\r\n const s = chord(chordName);\r\n const isSuperset = isSupersetOf(s.chroma);\r\n return entries$1()\r\n .filter(chord => isSuperset(chord.chroma))\r\n .map(chord => s.tonic + chord.aliases[0]);\r\n}\r\n/**\r\n * Find all chords names that are a subset of the given one\r\n * (has less notes but all from the given chord)\r\n *\r\n * @example\r\n */\r\nfunction reduced(chordName) {\r\n const s = chord(chordName);\r\n const isSubset = isSubsetOf(s.chroma);\r\n return entries$1()\r\n .filter(chord => isSubset(chord.chroma))\r\n .map(chord => s.tonic + chord.aliases[0]);\r\n}\n\nexport { chord, chordScales, extended, reduced, tokenize, transpose };\n//# sourceMappingURL=index.esnext.js.map\n","import { interval, coordToInterval } from '@tonaljs/core';\nexport { tokenizeInterval as tokenize } from '@tonaljs/core';\n\n/**\r\n * Get the natural list of names\r\n */\r\nfunction names() {\r\n return \"1P 2M 3M 4P 5P 6m 7m\".split(\" \");\r\n}\r\n/**\r\n * Get the simplified version of an interval.\r\n *\r\n * @function\r\n * @param {string} interval - the interval to simplify\r\n * @return {string} the simplified interval\r\n *\r\n * @example\r\n * simplify(\"9M\") // => \"2M\"\r\n * [\"8P\", \"9M\", \"10M\", \"11P\", \"12P\", \"13M\", \"14M\", \"15P\"].map(simplify)\r\n * // => [ \"8P\", \"2M\", \"3M\", \"4P\", \"5P\", \"6M\", \"7M\", \"8P\" ]\r\n * simplify(\"2M\") // => \"2M\"\r\n * simplify(\"-2M\") // => \"7m\"\r\n */\r\nfunction simplify(name) {\r\n const i = interval(name);\r\n return i.empty ? \"\" : i.simple + i.q;\r\n}\r\n/**\r\n * Get the inversion (https://en.wikipedia.org/wiki/Inversion_(music)#Intervals)\r\n * of an interval.\r\n *\r\n * @function\r\n * @param {string} interval - the interval to invert in interval shorthand\r\n * notation or interval array notation\r\n * @return {string} the inverted interval\r\n *\r\n * @example\r\n * invert(\"3m\") // => \"6M\"\r\n * invert(\"2M\") // => \"7m\"\r\n */\r\nfunction invert(name) {\r\n const i = interval(name);\r\n if (i.empty) {\r\n return \"\";\r\n }\r\n const step = (7 - i.step) % 7;\r\n const alt = i.type === \"perfectable\" ? -i.alt : -(i.alt + 1);\r\n return interval({ step, alt, oct: i.oct, dir: i.dir }).name;\r\n}\r\n// interval numbers\r\nconst IN = [1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7];\r\n// interval qualities\r\nconst IQ = \"P m M m M P d P m M m M\".split(\" \");\r\n/**\r\n * Get interval name from semitones number. Since there are several interval\r\n * names for the same number, the name it\"s arbitraty, but deterministic.\r\n *\r\n * @param {Integer} num - the number of semitones (can be negative)\r\n * @return {string} the interval name\r\n * @example\r\n * fromSemitones(7) // => \"5P\"\r\n * fromSemitones(-7) // => \"-5P\"\r\n */\r\nfunction fromSemitones(semitones) {\r\n const d = semitones < 0 ? -1 : 1;\r\n const n = Math.abs(semitones);\r\n const c = n % 12;\r\n const o = Math.floor(n / 12);\r\n return d * (IN[c] + 7 * o) + IQ[c];\r\n}\r\nfunction combine(fn) {\r\n return (a, b) => {\r\n const coordA = interval(a).coord;\r\n const coordB = interval(b).coord;\r\n if (coordA && coordB) {\r\n const coord = fn(coordA, coordB);\r\n return coordToInterval(coord).name;\r\n }\r\n };\r\n}\r\n/**\r\n * Adds two intervals\r\n *\r\n * @function\r\n * @param {string} interval1\r\n * @param {string} interval2\r\n * @return {string} the added interval name\r\n * @example\r\n * import { add } from \"@tonaljs/core\"\r\n * add(\"3m\", \"5P\") // => \"7m\"\r\n */\r\nconst add = combine((a, b) => [a[0] + b[0], a[1] + b[1]]);\r\n/**\r\n * Subtracts two intervals\r\n *\r\n * @function\r\n * @param {string} minuendInterval\r\n * @param {string} subtrahendInterval\r\n * @return {string} the substracted interval name\r\n * @example\r\n * import { substract } from '@tonaljs/core'\r\n * substract('5P', '3M') // => '3m'\r\n * substract('3M', '5P') // => '-3m'\r\n */\r\nconst substract = combine((a, b) => [a[0] - b[0], a[1] - b[1]]);\n\nexport { add, fromSemitones, invert, names, simplify, substract };\n//# sourceMappingURL=index.esnext.js.map\n","import { note } from '@tonaljs/core';\n\nfunction isMidi(arg) {\r\n return +arg >= 0 && +arg <= 127;\r\n}\r\n/**\r\n * Get the note midi number (a number between 0 and 127)\r\n *\r\n * It returns undefined if not valid note name\r\n *\r\n * @function\r\n * @param {string|number} note - the note name or midi number\r\n * @return {Integer} the midi number or undefined if not valid note\r\n * @example\r\n * import { toMidi } from '@tonaljs/midi'\r\n * toMidi(\"C4\") // => 60\r\n * toMidi(60) // => 60\r\n * toMidi('60') // => 60\r\n */\r\nfunction toMidi(note$1) {\r\n if (isMidi(note$1)) {\r\n return +note$1;\r\n }\r\n const n = note(note$1);\r\n return n.empty ? null : n.midi;\r\n}\r\n/**\r\n * Get the frequency in hertzs from midi number\r\n *\r\n * @param {number} midi - the note midi number\r\n * @param {number} [tuning = 440] - A4 tuning frequency in Hz (440 by default)\r\n * @return {number} the frequency or null if not valid note midi\r\n * @example\r\n * import { midiToFreq} from '@tonaljs/midi'\r\n * midiToFreq(69) // => 440\r\n */\r\nfunction midiToFreq(midi, tuning = 440) {\r\n return Math.pow(2, (midi - 69) / 12) * tuning;\r\n}\r\nconst L2 = Math.log(2);\r\nconst L440 = Math.log(440);\r\n/**\r\n * Get the midi number from a frequency in hertz. The midi number can\r\n * contain decimals (with two digits precission)\r\n *\r\n * @param {number} frequency\r\n * @return {number}\r\n * @example\r\n * import { freqToMidi} from '@tonaljs/midi'\r\n * freqToMidi(220)); //=> 57\r\n * freqToMidi(261.62)); //=> 60\r\n * freqToMidi(261)); //=> 59.96\r\n */\r\nfunction freqToMidi(freq) {\r\n const v = (12 * (Math.log(freq) - L440)) / L2 + 69;\r\n return Math.round(v * 100) / 100;\r\n}\r\nconst SHARPS = \"C C# D D# E F F# G G# A A# B\".split(\" \");\r\nconst FLATS = \"C Db D Eb E F Gb G Ab A Bb B\".split(\" \");\r\n/**\r\n * Given a midi number, returns a note name. The altered notes will have\r\n * flats unless explicitly set with the optional `useSharps` parameter.\r\n *\r\n * @function\r\n * @param {number} midi - the midi note number\r\n * @param {Object} options = default: `{ sharps: false, pitchClass: false }`\r\n * @param {boolean} useSharps - (Optional) set to true to use sharps instead of flats\r\n * @return {string} the note name\r\n * @example\r\n * import { midiToNoteName } from '@tonaljs/midi'\r\n * midiToNoteName(61) // => \"Db4\"\r\n * midiToNoteName(61, { pitchClass: true }) // => \"Db\"\r\n * midiToNoteName(61, { sharps: true }) // => \"C#4\"\r\n * midiToNoteName(61, { pitchClass: true, sharps: true }) // => \"C#\"\r\n * // it rounds to nearest note\r\n * midiToNoteName(61.7) // => \"D4\"\r\n */\r\nfunction midiToNoteName(midi, options = {}) {\r\n midi = Math.round(midi);\r\n const pcs = options.sharps === true ? SHARPS : FLATS;\r\n const pc = pcs[midi % 12];\r\n if (options.pitchClass) {\r\n return pc;\r\n }\r\n const o = Math.floor(midi / 12) - 1;\r\n return pc + o;\r\n}\n\nexport { freqToMidi, isMidi, midiToFreq, midiToNoteName, toMidi };\n//# sourceMappingURL=index.esnext.js.map\n","import { note, transpose, coordToNote } from '@tonaljs/core';\nexport { tokenizeNote as tokenize } from '@tonaljs/core';\nimport { midiToNoteName } from '@tonaljs/midi';\n\nconst toNoteName = (sameAccidentals) => (noteName) => {\r\n const n = note(noteName);\r\n if (n.empty) {\r\n return \"\";\r\n }\r\n const sharps = sameAccidentals ? n.alt > 0 : n.alt < 0;\r\n const pitchClass = n.midi === null;\r\n return midiToNoteName(n.midi || n.chroma, { sharps, pitchClass });\r\n};\r\n/**\r\n * Simplify a note\r\n *\r\n * @function\r\n * @param {string} note - the note to be simplified\r\n * - sameAccType: default true. Use same kind of accidentals that source\r\n * @return {string} the simplfied note or '' if not valid note\r\n * @example\r\n * simplify(\"C##\") // => \"D\"\r\n * simplify(\"C###\") // => \"D#\"\r\n * simplify(\"C###\")\r\n * simplify(\"B#4\") // => \"C5\"\r\n */\r\nconst simplify = toNoteName(true);\r\n/**\r\n * Get enharmonic of a note\r\n *\r\n * @function\r\n * @param {string} note\r\n * @return {string} the enhramonic note or '' if not valid note\r\n * @example\r\n * Note.enharmonic(\"Db\") // => \"C#\"\r\n * Note.enhramonic(\"C\") // => \"C\"\r\n */\r\nconst enharmonic = toNoteName(false);\r\n/**\r\n * Transpose by an interval\r\n * @function\r\n * @param {string} interval\r\n * @return {function} a function that transposes by the given interval\r\n * @example\r\n * [\"C\", \"D\", \"E\"].map(transposeBy(\"5P\"));\r\n * // => [\"G\", \"A\", \"B\"]\r\n */\r\nconst transposeBy = (interval) => (note) => transpose(note, interval);\r\n/**\r\n * Transpose from a note\r\n * @function\r\n * @param {string} note\r\n * @return {function} a function that transposes the the note by an interval\r\n * [\"1P\", \"3M\", \"5P\"].map(transposeFrom(\"C\"));\r\n * // => [\"C\", \"E\", \"G\"]\r\n */\r\nconst transposeFrom = (note) => (interval) => transpose(note, interval);\r\n/**\r\n * Transpose a note by a number of perfect fifths.\r\n *\r\n * @function\r\n * @param {string} note - the note name\r\n * @param {number} fifhts - the number of fifths\r\n * @return {string} the transposed note name\r\n *\r\n * @example\r\n * import { transposeFifths } from \"@tonaljs/note\"\r\n * transposeFifths(\"G4\", 1) // => \"D\"\r\n * [0, 1, 2, 3, 4].map(fifths => transposeFifths(\"C\", fifths)) // => [\"C\", \"G\", \"D\", \"A\", \"E\"]\r\n */\r\nfunction transposeFifths(noteName, fifths) {\r\n const n = note(noteName);\r\n if (n.empty) {\r\n return \"\";\r\n }\r\n const [nFifths, nOcts] = n.coord;\r\n const transposed = nOcts === undefined\r\n ? coordToNote([nFifths + fifths])\r\n : coordToNote([nFifths + fifths, nOcts]);\r\n return transposed.name;\r\n}\n\nexport { enharmonic, simplify, transposeBy, transposeFifths, transposeFrom };\n//# sourceMappingURL=index.esnext.js.map\n","import { isPitch, altToAcc, isNamed, accToAlt, interval } from '@tonaljs/core';\n\nconst NoRomanNumeral = { empty: true, name: \"\", chordType: \"\" };\r\nconst cache = {};\r\n/**\r\n * Get properties of a roman numeral string\r\n *\r\n * @function\r\n * @param {string} - the roman numeral string (can have type, like: Imaj7)\r\n * @return {Object} - the roman numeral properties\r\n * @param {string} name - the roman numeral (tonic)\r\n * @param {string} type - the chord type\r\n * @param {string} num - the number (1 = I, 2 = II...)\r\n * @param {boolean} major - major or not\r\n *\r\n * @example\r\n * romanNumeral(\"VIIb5\") // => { name: \"VII\", type: \"b5\", num: 7, major: true }\r\n */\r\nfunction romanNumeral(src) {\r\n return typeof src === \"string\"\r\n ? cache[src] || (cache[src] = parse(src))\r\n : typeof src === \"number\"\r\n ? romanNumeral(NAMES[src] || \"\")\r\n : isPitch(src)\r\n ? fromPitch(src)\r\n : isNamed(src)\r\n ? romanNumeral(src.name)\r\n : NoRomanNumeral;\r\n}\r\n/**\r\n * Get roman numeral names\r\n *\r\n * @function\r\n * @param {boolean} [isMajor=true]\r\n * @return {Array}\r\n *\r\n * @example\r\n * names() // => [\"I\", \"II\", \"III\", \"IV\", \"V\", \"VI\", \"VII\"]\r\n */\r\nfunction names(major = true) {\r\n return (major ? NAMES : NAMES_MINOR).slice();\r\n}\r\nfunction fromPitch(pitch) {\r\n return romanNumeral(altToAcc(pitch.alt) + NAMES[pitch.step]);\r\n}\r\nconst REGEX = /^(#{1,}|b{1,}|x{1,}|)(IV|I{1,3}|VI{0,2}|iv|i{1,3}|vi{0,2})([^IViv]*)$/;\r\nfunction tokenize(str) {\r\n return (REGEX.exec(str) || [\"\", \"\", \"\", \"\"]);\r\n}\r\nconst ROMANS = \"I II III IV V VI VII\";\r\nconst NAMES = ROMANS.split(\" \");\r\nconst NAMES_MINOR = ROMANS.toLowerCase().split(\" \");\r\nfunction parse(src) {\r\n const [name, acc, roman, chordType] = tokenize(src);\r\n if (!roman) {\r\n return NoRomanNumeral;\r\n }\r\n const upperRoman = roman.toUpperCase();\r\n const step = NAMES.indexOf(upperRoman);\r\n const alt = accToAlt(acc);\r\n const dir = 1;\r\n return {\r\n empty: false,\r\n name,\r\n roman,\r\n interval: interval({ step, alt, dir }).name,\r\n acc,\r\n chordType,\r\n alt,\r\n step,\r\n major: roman === upperRoman,\r\n oct: 0,\r\n dir\r\n };\r\n}\n\nexport { names, romanNumeral, tokenize };\n//# sourceMappingURL=index.esnext.js.map\n","import { transpose, altToAcc, accToAlt, note } from '@tonaljs/core';\nimport { transposeFifths } from '@tonaljs/note';\nimport { romanNumeral } from '@tonaljs/roman-numeral';\n\nconst mapToScale = (scale) => (symbols, sep = \"\") => symbols.map((symbol, index) => symbol !== \"-\" ? scale[index] + sep + symbol : \"\");\r\nfunction keyScale(gradesLiteral, chordsLiteral, hfLiteral, chordScalesLiteral) {\r\n return (tonic) => {\r\n const grades = gradesLiteral.split(\" \");\r\n const intervals = grades.map(gr => romanNumeral(gr).interval || \"\");\r\n const scale = intervals.map(interval => transpose(tonic, interval));\r\n const map = mapToScale(scale);\r\n return {\r\n tonic,\r\n grades,\r\n intervals,\r\n scale,\r\n chords: map(chordsLiteral.split(\" \")),\r\n chordsHarmonicFunction: hfLiteral.split(\" \"),\r\n chordScales: map(chordScalesLiteral.split(\",\"), \" \")\r\n };\r\n };\r\n}\r\nconst distInFifths = (from, to) => {\r\n const f = note(from);\r\n const t = note(to);\r\n return f.empty || t.empty ? 0 : t.coord[0] - f.coord[0];\r\n};\r\nconst MajorScale = keyScale(\"I II III IV V VI VII\", \"maj7 m7 m7 maj7 7 m7 m7b5\", \"T SD T SD D T D\", \"major,dorian,phrygian,lydian,mixolydian,minor,locrian\");\r\nconst NaturalScale = keyScale(\"I II bIII IV V bVI bVII\", \"m7 m7b5 maj7 m7 m7 maj7 7\", \"T SD T SD D SD SD\", \"minor,locrian,major,dorian,phrygian,lydian,mixolydian\");\r\nconst HarmonicScale = keyScale(\"I II bIII IV V bVI VII\", \"mmaj7 m7b5 +maj7 m7 7 maj7 mo7\", \"T SD T SD D SD D\", \"harmonic minor,locrian 6,major augmented,lydian diminished,phrygian dominant,lydian #9,ultralocrian\");\r\nconst MelodicScale = keyScale(\"I II bIII IV V VI VII\", \"m6 m7 +maj7 7 7 m7b5 m7b5\", \"T SD T SD D - -\", \"melodic minor,dorian b2,lydian augmented,lydian dominant,mixolydian b6,locrian #2,altered\");\r\n/**\r\n * Get a major key properties in a given tonic\r\n * @param tonic\r\n */\r\nfunction majorKey(tonic) {\r\n const keyScale = MajorScale(tonic);\r\n const alteration = distInFifths(\"C\", tonic);\r\n const map = mapToScale(keyScale.scale);\r\n return {\r\n ...keyScale,\r\n type: \"major\",\r\n minorRelative: transpose(tonic, \"-3m\"),\r\n alteration,\r\n keySignature: altToAcc(alteration),\r\n secondaryDominants: map(\"- VI7 VII7 I7 II7 III7 -\".split(\" \")),\r\n secondaryDominantsMinorRelative: map(\"- IIIm7b5 IV#m7 Vm7 VIm7 VIIm7b5 -\".split(\" \")),\r\n substituteDominants: map(\"- bIII7 IV7 bV7 bVI7 bVII7 -\".split(\" \")),\r\n substituteDominantsMinorRelative: map(\"- IIIm7 Im7 IIbm7 VIm7 IVm7 -\".split(\" \"))\r\n };\r\n}\r\n/**\r\n * Get minor key properties in a given tonic\r\n * @param tonic\r\n */\r\nfunction minorKey(tonic) {\r\n const alteration = distInFifths(\"C\", tonic) - 3;\r\n return {\r\n type: \"minor\",\r\n tonic,\r\n relativeMajor: transpose(tonic, \"3m\"),\r\n alteration,\r\n keySignature: altToAcc(alteration),\r\n natural: NaturalScale(tonic),\r\n harmonic: HarmonicScale(tonic),\r\n melodic: MelodicScale(tonic)\r\n };\r\n}\r\n/**\r\n * Given a key signature, returns the tonic of the major key\r\n * @param sigature\r\n * @example\r\n * majorTonicFromKeySignature('###') // => 'A'\r\n */\r\nfunction majorTonicFromKeySignature(sig) {\r\n if (typeof sig === \"number\") {\r\n return transposeFifths(\"C\", sig);\r\n }\r\n else if (typeof sig === \"string\" && /^b+|#+$/.test(sig)) {\r\n return transposeFifths(\"C\", accToAlt(sig));\r\n }\r\n return null;\r\n}\n\nexport { majorKey, majorTonicFromKeySignature, minorKey };\n//# sourceMappingURL=index.esnext.js.map\n","import { chromaToIntervals, EmptyPcset } from '@tonaljs/pcset';\n\nconst DATA = [\r\n [0, 2773, 0, \"ionian\", \"\", \"Maj7\", \"major\"],\r\n [1, 2902, 2, \"dorian\", \"m\", \"m7\"],\r\n [2, 3418, 4, \"phrygian\", \"m\", \"m7\"],\r\n [3, 2741, -1, \"lydian\", \"\", \"Maj7\"],\r\n [4, 2774, 1, \"mixolydian\", \"\", \"7\"],\r\n [5, 2906, 3, \"aeolian\", \"m\", \"m7\", \"minor\"],\r\n [6, 3434, 5, \"locrian\", \"dim\", \"m7b5\"]\r\n];\n\nconst NoMode = {\r\n ...EmptyPcset,\r\n name: \"\",\r\n alt: 0,\r\n modeNum: NaN,\r\n triad: \"\",\r\n seventh: \"\",\r\n aliases: []\r\n};\r\nconst all = DATA.map(toMode);\r\nconst index = {};\r\nall.forEach(mode => {\r\n index[mode.name] = mode;\r\n mode.aliases.forEach(alias => {\r\n index[alias] = mode;\r\n });\r\n});\r\n/**\r\n * Get a Mode by it's name\r\n *\r\n * @example\r\n * mode('dorian')\r\n * // =>\r\n * // {\r\n * // intervals: [ '1P', '2M', '3m', '4P', '5P', '6M', '7m' ],\r\n * // modeNum: 1,\r\n * // chroma: '101101010110',\r\n * // normalized: '101101010110',\r\n * // name: 'dorian',\r\n * // setNum: 2902,\r\n * // alt: 2,\r\n * // triad: 'm',\r\n * // seventh: 'm7',\r\n * // aliases: []\r\n * // }\r\n */\r\nfunction mode(name) {\r\n return typeof name === \"string\"\r\n ? index[name.toLowerCase()] || NoMode\r\n : name && name.name\r\n ? mode(name.name)\r\n : NoMode;\r\n}\r\n/**\r\n * Get a list of all know modes\r\n */\r\nfunction entries() {\r\n return all.slice();\r\n}\r\nfunction toMode(mode) {\r\n const [modeNum, setNum, alt, name, triad, seventh, alias] = mode;\r\n const aliases = alias ? [alias] : [];\r\n const chroma = Number(setNum).toString(2);\r\n const intervals = chromaToIntervals(chroma);\r\n return {\r\n empty: false,\r\n intervals,\r\n modeNum,\r\n chroma,\r\n normalized: chroma,\r\n name,\r\n setNum,\r\n alt,\r\n triad,\r\n seventh,\r\n aliases\r\n };\r\n}\n\nexport { entries, mode };\n//# sourceMappingURL=index.esnext.js.map\n","import { tokenize } from '@tonaljs/chord';\nimport { transpose, interval, distance } from '@tonaljs/core';\nimport { romanNumeral } from '@tonaljs/roman-numeral';\n\n/**\r\n * Given a tonic and a chord list expressed with roman numeral notation\r\n * returns the progression expressed with leadsheet chords symbols notation\r\n * @example\r\n * fromRomanNumerals(\"C\", [\"I\", \"IIm7\", \"V7\"]);\r\n * // => [\"C\", \"Dm7\", \"G7\"]\r\n */\r\nfunction fromRomanNumerals(tonic, chords) {\r\n const romanNumerals = chords.map(romanNumeral);\r\n return romanNumerals.map(rn => transpose(tonic, interval(rn)) + rn.chordType);\r\n}\r\n/**\r\n * Given a tonic and a chord list with leadsheet symbols notation,\r\n * return the chord list with roman numeral notation\r\n * @example\r\n * toRomanNumerals(\"C\", [\"CMaj7\", \"Dm7\", \"G7\"]);\r\n * // => [\"IMaj7\", \"IIm7\", \"V7\"]\r\n */\r\nfunction toRomanNumerals(tonic, chords) {\r\n return chords.map(chord => {\r\n const [note, chordType] = tokenize(chord);\r\n const intervalName = distance(tonic, note);\r\n const roman = romanNumeral(interval(intervalName));\r\n return roman.name + chordType;\r\n });\r\n}\n\nexport { fromRomanNumerals, toRomanNumerals };\n//# sourceMappingURL=index.esnext.js.map\n","import { compact, range } from '@tonaljs/array';\nimport { toMidi, midiToNoteName } from '@tonaljs/midi';\n\n/**\r\n * Create a numeric range. You supply a list of notes or numbers and it will\r\n * be conected to create complex ranges.\r\n *\r\n * @param {Array} array - the list of notes or numbers used\r\n * @return {Array} an array of numbers or empty array if not vald parameters\r\n *\r\n * @example\r\n * numeric([\"C5\", \"C4\"]) // => [ 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60 ]\r\n * // it works midi notes\r\n * numeric([10, 5]) // => [ 10, 9, 8, 7, 6, 5 ]\r\n * // complex range\r\n * numeric([\"C4\", \"E4\", \"Bb3\"]) // => [60, 61, 62, 63, 64, 63, 62, 61, 60, 59, 58]\r\n */\r\nfunction numeric(notes) {\r\n const midi = compact(notes.map(toMidi));\r\n if (!notes.length || midi.length !== notes.length) {\r\n // there is no valid notes\r\n return [];\r\n }\r\n return midi.reduce((result, note) => {\r\n const last = result[result.length - 1];\r\n return result.concat(range(last, note).slice(1));\r\n }, [midi[0]]);\r\n}\r\n/**\r\n * Create a range of chromatic notes. The altered notes will use flats.\r\n *\r\n * @function\r\n * @param {String|Array} list - the list of notes or midi note numbers\r\n * @return {Array} an array of note names\r\n *\r\n * @example\r\n * Range.chromatic(\"C2 E2 D2\") // => [\"C2\", \"Db2\", \"D2\", \"Eb2\", \"E2\", \"Eb2\", \"D2\"]\r\n * // with sharps\r\n * Range.chromatic(\"C2 C3\", true) // => [ \"C2\", \"C#2\", \"D2\", \"D#2\", \"E2\", \"F2\", \"F#2\", \"G2\", \"G#2\", \"A2\", \"A#2\", \"B2\", \"C3\" ]\r\n */\r\nfunction chromatic(notes, options) {\r\n return numeric(notes).map(midi => midiToNoteName(midi, options));\r\n}\n\nexport { chromatic, numeric };\n//# sourceMappingURL=index.esnext.js.map\n","import { sortedUniqNoteNames, rotate } from '@tonaljs/array';\nimport { entries } from '@tonaljs/chord-dictionary';\nimport { note, transpose } from '@tonaljs/core';\nimport { isSubsetOf, isSupersetOf, modes } from '@tonaljs/pcset';\nimport { get, entries as entries$1 } from '@tonaljs/scale-dictionary';\n\n/**\r\n * References:\r\n * - https://www.researchgate.net/publication/327567188_An_Algorithm_for_Spelling_the_Pitches_of_Any_Musical_Scale\r\n * @module scale\r\n */\r\nconst NoScale = {\r\n empty: true,\r\n name: \"\",\r\n type: \"\",\r\n tonic: null,\r\n setNum: NaN,\r\n chroma: \"\",\r\n normalized: \"\",\r\n aliases: [],\r\n notes: [],\r\n intervals: []\r\n};\r\n/**\r\n * Given a string with a scale name and (optionally) a tonic, split\r\n * that components.\r\n *\r\n * It retuns an array with the form [ name, tonic ] where tonic can be a\r\n * note name or null and name can be any arbitrary string\r\n * (this function doesn\"t check if that scale name exists)\r\n *\r\n * @function\r\n * @param {string} name - the scale name\r\n * @return {Array} an array [tonic, name]\r\n * @example\r\n * tokenize(\"C mixolydean\") // => [\"C\", \"mixolydean\"]\r\n * tokenize(\"anything is valid\") // => [\"\", \"anything is valid\"]\r\n * tokenize() // => [\"\", \"\"]\r\n */\r\nfunction tokenize(name) {\r\n if (typeof name !== \"string\") {\r\n return [\"\", \"\"];\r\n }\r\n const i = name.indexOf(\" \");\r\n const tonic = note(name.substring(0, i));\r\n if (tonic.empty) {\r\n const n = note(name);\r\n return n.empty ? [\"\", name] : [n.name, \"\"];\r\n }\r\n const type = name.substring(tonic.name.length + 1);\r\n return [tonic.name, type.length ? type : \"\"];\r\n}\r\n/**\r\n * Get a Scale from a scale name.\r\n */\r\nfunction scale(src) {\r\n const tokens = Array.isArray(src) ? src : tokenize(src);\r\n const tonic = note(tokens[0]).name;\r\n const st = get(tokens[1]);\r\n if (st.empty) {\r\n return NoScale;\r\n }\r\n const type = st.name;\r\n const notes = tonic\r\n ? st.intervals.map(i => transpose(tonic, i))\r\n : [];\r\n const name = tonic ? tonic + \" \" + type : type;\r\n return { ...st, name, type, tonic, notes };\r\n}\r\n/**\r\n * Get all chords that fits a given scale\r\n *\r\n * @function\r\n * @param {string} name - the scale name\r\n * @return {Array} - the chord names\r\n *\r\n * @example\r\n * scaleChords(\"pentatonic\") // => [\"5\", \"64\", \"M\", \"M6\", \"Madd9\", \"Msus2\"]\r\n */\r\nfunction scaleChords(name) {\r\n const s = scale(name);\r\n const inScale = isSubsetOf(s.chroma);\r\n return entries()\r\n .filter(chord => inScale(chord.chroma))\r\n .map(chord => chord.aliases[0]);\r\n}\r\n/**\r\n * Get all scales names that are a superset of the given one\r\n * (has the same notes and at least one more)\r\n *\r\n * @function\r\n * @param {string} name\r\n * @return {Array} a list of scale names\r\n * @example\r\n * extended(\"major\") // => [\"bebop\", \"bebop dominant\", \"bebop major\", \"chromatic\", \"ichikosucho\"]\r\n */\r\nfunction extended(name) {\r\n const s = scale(name);\r\n const isSuperset = isSupersetOf(s.chroma);\r\n return entries$1()\r\n .filter(scale => isSuperset(scale.chroma))\r\n .map(scale => scale.name);\r\n}\r\n/**\r\n * Find all scales names that are a subset of the given one\r\n * (has less notes but all from the given scale)\r\n *\r\n * @function\r\n * @param {string} name\r\n * @return {Array} a list of scale names\r\n *\r\n * @example\r\n * reduced(\"major\") // => [\"ionian pentatonic\", \"major pentatonic\", \"ritusen\"]\r\n */\r\nfunction reduced(name) {\r\n const isSubset = isSubsetOf(scale(name).chroma);\r\n return entries$1()\r\n .filter(scale => isSubset(scale.chroma))\r\n .map(scale => scale.name);\r\n}\r\n/**\r\n * Given an array of notes, return the scale: a pitch class set starting from\r\n * the first note of the array\r\n *\r\n * @function\r\n * @param {string[]} notes\r\n * @return {string[]} pitch classes with same tonic\r\n * @example\r\n * scaleNotes(['C4', 'c3', 'C5', 'C4', 'c4']) // => [\"C\"]\r\n * scaleNotes(['D4', 'c#5', 'A5', 'F#6']) // => [\"D\", \"F#\", \"A\", \"C#\"]\r\n */\r\nfunction scaleNotes(notes) {\r\n const pcset = notes.map(n => note(n).pc).filter(x => x);\r\n const tonic = pcset[0];\r\n const scale = sortedUniqNoteNames(pcset);\r\n return rotate(scale.indexOf(tonic), scale);\r\n}\r\n/**\r\n * Find mode names of a scale\r\n *\r\n * @function\r\n * @param {string} name - scale name\r\n * @example\r\n * modeNames(\"C pentatonic\") // => [\r\n * [\"C\", \"major pentatonic\"],\r\n * [\"D\", \"egyptian\"],\r\n * [\"E\", \"malkos raga\"],\r\n * [\"G\", \"ritusen\"],\r\n * [\"A\", \"minor pentatonic\"]\r\n * ]\r\n */\r\nfunction modeNames(name) {\r\n const s = scale(name);\r\n if (s.empty) {\r\n return [];\r\n }\r\n const tonics = s.tonic ? s.notes : s.intervals;\r\n return modes(s.chroma)\r\n .map((chroma, i) => {\r\n const modeName = scale(chroma).name;\r\n return modeName ? [tonics[i], modeName] : [\"\", \"\"];\r\n })\r\n .filter(x => x[0]);\r\n}\n\nexport { extended, modeNames, reduced, scale, scaleChords, scaleNotes, tokenize };\n//# sourceMappingURL=index.esnext.js.map\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tonaljs/abc-notation'), require('@tonaljs/array'), require('@tonaljs/chord'), require('@tonaljs/chord-dictionary'), require('@tonaljs/core'), require('@tonaljs/interval'), require('@tonaljs/key'), require('@tonaljs/midi'), require('@tonaljs/mode'), require('@tonaljs/note'), require('@tonaljs/pcset'), require('@tonaljs/progression'), require('@tonaljs/range'), require('@tonaljs/roman-numeral'), require('@tonaljs/scale'), require('@tonaljs/scale-dictionary')) :\n typeof define === 'function' && define.amd ? define(['exports', '@tonaljs/abc-notation', '@tonaljs/array', '@tonaljs/chord', '@tonaljs/chord-dictionary', '@tonaljs/core', '@tonaljs/interval', '@tonaljs/key', '@tonaljs/midi', '@tonaljs/mode', '@tonaljs/note', '@tonaljs/pcset', '@tonaljs/progression', '@tonaljs/range', '@tonaljs/roman-numeral', '@tonaljs/scale', '@tonaljs/scale-dictionary'], factory) :\n (global = global || self, factory(global.Tonal = {}, global.abcNotation, global.array, global.chord, global.chordDictionary, global.Core, global.interval, global.key, global.midi, global.mode, global.note, global.pcset, global.progression, global.range, global.romanNumeral, global.scale, global.scaleDictionary));\n}(this, (function (exports, abcNotation, array, chord, chordDictionary, Core, interval, key, midi, mode, note, pcset, progression, range, romanNumeral, scale, scaleDictionary) { 'use strict';\n\n // backwards compatibility\r\n var Tonal = Core;\n\n Object.keys(Core).forEach(function (k) {\n if (k !== 'default') Object.defineProperty(exports, k, {\n enumerable: true,\n get: function () {\n return Core[k];\n }\n });\n });\n exports.AbcNotation = abcNotation;\n exports.Array = array;\n exports.Chord = chord;\n exports.ChordDictionary = chordDictionary;\n exports.Core = Core;\n exports.Interval = interval;\n exports.Key = key;\n exports.Midi = midi;\n exports.Mode = mode;\n exports.Note = note;\n exports.PcSet = pcset;\n exports.Progression = progression;\n exports.Range = range;\n exports.RomanNumeral = romanNumeral;\n exports.Scale = scale;\n exports.ScaleDictionary = scaleDictionary;\n exports.Tonal = Tonal;\n\n Object.defineProperty(exports, '__esModule', { value: true });\n\n})));\n//# sourceMappingURL=index.es5.js.map\n"],"names":["isNamed","src","name","isPitch","pitch","step","alt","FIFTHS","STEPS_TO_OCTS","map","fifths","Math","floor","encode","oct","dir","f","undefined","FIFTHS_TO_STEPS","decode","coord","o","i","unaltered","NoNote","empty","pc","acc","cache","fillStr","s","n","Array","join","altToAcc","accToAlt","length","note","noteName","tokens","tokenize","letter","octStr","charCodeAt","chroma","SEMI","height","midi","freq","pow","parse","props","charAt","stepToLetter","pitchName","REGEX","str","m","exec","toUpperCase","replace","coordToNote","noteCoord","NoInterval","REGEX$1","RegExp","tokenize$1","cache$1","interval","num","q","abs","t","type","simple","test","qToAlt","semitones","SIZES","parse$1","fillStr$1","altToQ","pitchName$1","coordToInterval","transpose","intervalName","note$1","interval$1","intervalCoord","distance","fromNote","toNote","from","to","fcoord","tcoord","character","times","abcToScientificNotation","a","scientificToAbcNotation","toLowerCase","transpose$1","range","b","ascR","descR","rotate","arr","len","slice","concat","compact","filter","sortedNoteNames","notes","sort","sortedUniqNoteNames","permutations","reduce","perm","e","pos","newPerm","splice","rnd","random","EmptyPcset","setNum","normalized","intervals","setNumToChroma","Number","toString","chromaToNumber","parseInt","isChroma","set","[object Object]","pcset","isArray","binary","listToChroma","isPcset","normalizedNum","split","_","chromaRotations","chromaToIntervals","chromaToPcset","IVLS","push","all","modes","normalize","r","isSubsetOf","isSupersetOf","isNoteIncludedInSet","includes","isIncluded","s1","s2","NoChordType","quality","aliases","chords","index","get","entries","add","fullName","has","indexOf","getQuality","chord","forEach","alias","addAlias","ivls","names","console","warn","Object","keys","NoScaleType","scales","scale","NoChord","tonic","NaN","NUM_TYPES","tokenizeNote","findChord","isChordIncluded","chordName","isSuperset","entries$1","isSubset","IN","IQ","combine","fn","coordA","coordB","substract","d","c","isMidi","arg","toMidi","L2","log","L440","SHARPS","FLATS","midiToNoteName","options","round","sharps","pitchClass","v","tuning","toNoteName","sameAccidentals","simplify","enharmonic","transposeFifths","nFifths","nOcts","NoRomanNumeral","chordType","romanNumeral","roman","upperRoman","NAMES","major","ROMANS","NAMES_MINOR","mapToScale","symbols","sep","symbol","keyScale","gradesLiteral","chordsLiteral","hfLiteral","chordScalesLiteral","grades","gr","chordsHarmonicFunction","chordScales","distInFifths","MajorScale","NaturalScale","HarmonicScale","MelodicScale","alteration","minorRelative","keySignature","secondaryDominants","secondaryDominantsMinorRelative","substituteDominants","substituteDominantsMinorRelative","sig","relativeMajor","natural","harmonic","melodic","NoMode","modeNum","triad","seventh","mode","rn","numeric","result","last","NoScale","substring","st","tonics","modeName","x","inScale","exports","abcNotation","array","chordDictionary","Core","key","progression","scaleDictionary","Tonal","k","defineProperty","enumerable","AbcNotation","Chord","ChordDictionary","Interval","Key","Midi","Mode","Note","PcSet","Progression","Range","RomanNumeral","Scale","ScaleDictionary","value","factory","require$$0","require$$1","require$$2","require$$3","require$$4","require$$5","require$$6","require$$7","require$$8","require$$9","require$$10","require$$11","require$$12","require$$13","require$$14","require$$15"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"tonal.min.js","sources":["../../core/dist/index.esnext.js","../../abc-notation/dist/index.esnext.js","../../array/dist/index.esnext.js","../../collection/dist/index.esnext.js","../../pcset/dist/index.esnext.js","../../chord-type/dist/index.esnext.js","../../chord-detect/dist/index.esnext.js","../../scale-type/dist/index.esnext.js","../../chord/dist/index.esnext.js","../../interval/dist/index.esnext.js","../../midi/dist/index.esnext.js","../../note/dist/index.esnext.js","../../roman-numeral/dist/index.esnext.js","../../key/dist/index.esnext.js","../../mode/dist/index.esnext.js","../../progression/dist/index.esnext.js","../../range/dist/index.esnext.js","../../scale/dist/index.esnext.js","../dist/index.es5.js"],"sourcesContent":["/**\r\n * Fill a string with a repeated character\r\n *\r\n * @param character\r\n * @param repetition\r\n */\r\nconst fillStr = (s, n) => Array(Math.abs(n) + 1).join(s);\r\nfunction deprecate(original, alternative, fn) {\r\n return function (...args) {\r\n // tslint:disable-next-line\r\n console.warn(`${original} is deprecated. Use ${alternative}.`);\r\n return fn.apply(this, args);\r\n };\r\n}\n\nfunction isNamed(src) {\r\n return src !== null && typeof src === \"object\" && typeof src.name === \"string\"\r\n ? true\r\n : false;\r\n}\n\nfunction isPitch(pitch) {\r\n return pitch !== null &&\r\n typeof pitch === \"object\" &&\r\n typeof pitch.step === \"number\" &&\r\n typeof pitch.alt === \"number\"\r\n ? true\r\n : false;\r\n}\r\n// The number of fifths of [C, D, E, F, G, A, B]\r\nconst FIFTHS = [0, 2, 4, -1, 1, 3, 5];\r\n// The number of octaves it span each step\r\nconst STEPS_TO_OCTS = FIFTHS.map((fifths) => Math.floor((fifths * 7) / 12));\r\nfunction encode(pitch) {\r\n const { step, alt, oct, dir = 1 } = pitch;\r\n const f = FIFTHS[step] + 7 * alt;\r\n if (oct === undefined) {\r\n return [dir * f];\r\n }\r\n const o = oct - STEPS_TO_OCTS[step] - 4 * alt;\r\n return [dir * f, dir * o];\r\n}\r\n// We need to get the steps from fifths\r\n// Fifths for CDEFGAB are [ 0, 2, 4, -1, 1, 3, 5 ]\r\n// We add 1 to fifths to avoid negative numbers, so:\r\n// for [\"F\", \"C\", \"G\", \"D\", \"A\", \"E\", \"B\"] we have:\r\nconst FIFTHS_TO_STEPS = [3, 0, 4, 1, 5, 2, 6];\r\nfunction decode(coord) {\r\n const [f, o, dir] = coord;\r\n const step = FIFTHS_TO_STEPS[unaltered(f)];\r\n const alt = Math.floor((f + 1) / 7);\r\n if (o === undefined) {\r\n return { step, alt, dir };\r\n }\r\n const oct = o + 4 * alt + STEPS_TO_OCTS[step];\r\n return { step, alt, oct, dir };\r\n}\r\n// Return the number of fifths as if it were unaltered\r\nfunction unaltered(f) {\r\n const i = (f + 1) % 7;\r\n return i < 0 ? 7 + i : i;\r\n}\n\nconst NoNote = { empty: true, name: \"\", pc: \"\", acc: \"\" };\r\nconst cache = new Map();\r\nconst stepToLetter = (step) => \"CDEFGAB\".charAt(step);\r\nconst altToAcc = (alt) => alt < 0 ? fillStr(\"b\", -alt) : fillStr(\"#\", alt);\r\nconst accToAlt = (acc) => acc[0] === \"b\" ? -acc.length : acc.length;\r\n/**\r\n * Given a note literal (a note name or a note object), returns the Note object\r\n * @example\r\n * note('Bb4') // => { name: \"Bb4\", midi: 70, chroma: 10, ... }\r\n */\r\nfunction note(src) {\r\n const cached = cache.get(src);\r\n if (cached) {\r\n return cached;\r\n }\r\n const value = typeof src === \"string\"\r\n ? parse(src)\r\n : isPitch(src)\r\n ? note(pitchName(src))\r\n : isNamed(src)\r\n ? note(src.name)\r\n : NoNote;\r\n cache.set(src, value);\r\n return value;\r\n}\r\nconst REGEX = /^([a-gA-G]?)(#{1,}|b{1,}|x{1,}|)(-?\\d*)\\s*(.*)$/;\r\n/**\r\n * @private\r\n */\r\nfunction tokenizeNote(str) {\r\n const m = REGEX.exec(str);\r\n return [m[1].toUpperCase(), m[2].replace(/x/g, \"##\"), m[3], m[4]];\r\n}\r\n/**\r\n * @private\r\n */\r\nfunction coordToNote(noteCoord) {\r\n return note(decode(noteCoord));\r\n}\r\nconst SEMI = [0, 2, 4, 5, 7, 9, 11];\r\nfunction parse(noteName) {\r\n const tokens = tokenizeNote(noteName);\r\n if (tokens[0] === \"\" || tokens[3] !== \"\") {\r\n return NoNote;\r\n }\r\n const letter = tokens[0];\r\n const acc = tokens[1];\r\n const octStr = tokens[2];\r\n const step = (letter.charCodeAt(0) + 3) % 7;\r\n const alt = accToAlt(acc);\r\n const oct = octStr.length ? +octStr : undefined;\r\n const coord = encode({ step, alt, oct });\r\n const name = letter + acc + octStr;\r\n const pc = letter + acc;\r\n const chroma = (SEMI[step] + alt + 120) % 12;\r\n const o = oct === undefined ? -100 : oct;\r\n const height = SEMI[step] + alt + 12 * (o + 1);\r\n const midi = height >= 0 && height <= 127 ? height : null;\r\n const freq = oct === undefined ? null : Math.pow(2, (height - 69) / 12) * 440;\r\n return {\r\n empty: false,\r\n acc,\r\n alt,\r\n chroma,\r\n coord,\r\n freq,\r\n height,\r\n letter,\r\n midi,\r\n name,\r\n oct,\r\n pc,\r\n step\r\n };\r\n}\r\nfunction pitchName(props) {\r\n const { step, alt, oct } = props;\r\n const letter = stepToLetter(step);\r\n if (!letter) {\r\n return \"\";\r\n }\r\n const pc = letter + altToAcc(alt);\r\n return oct || oct === 0 ? pc + oct : pc;\r\n}\n\nconst NoInterval = { empty: true, name: \"\", acc: \"\" };\r\n// shorthand tonal notation (with quality after number)\r\nconst INTERVAL_TONAL_REGEX = \"([-+]?\\\\d+)(d{1,4}|m|M|P|A{1,4})\";\r\n// standard shorthand notation (with quality before number)\r\nconst INTERVAL_SHORTHAND_REGEX = \"(AA|A|P|M|m|d|dd)([-+]?\\\\d+)\";\r\nconst REGEX$1 = new RegExp(\"^\" + INTERVAL_TONAL_REGEX + \"|\" + INTERVAL_SHORTHAND_REGEX + \"$\");\r\n/**\r\n * @private\r\n */\r\nfunction tokenizeInterval(str) {\r\n const m = REGEX$1.exec(`${str}`);\r\n if (m === null) {\r\n return [\"\", \"\"];\r\n }\r\n return m[1] ? [m[1], m[2]] : [m[4], m[3]];\r\n}\r\nconst cache$1 = {};\r\n/**\r\n * Get interval properties. It returns an object with:\r\n *\r\n * - name: the interval name\r\n * - num: the interval number\r\n * - type: 'perfectable' or 'majorable'\r\n * - q: the interval quality (d, m, M, A)\r\n * - dir: interval direction (1 ascending, -1 descending)\r\n * - simple: the simplified number\r\n * - semitones: the size in semitones\r\n * - chroma: the interval chroma\r\n *\r\n * @param {string} interval - the interval name\r\n * @return {Object} the interval properties\r\n *\r\n * @example\r\n * import { interval } from '@tonaljs/core'\r\n * interval('P5').semitones // => 7\r\n * interval('m3').type // => 'majorable'\r\n */\r\nfunction interval(src) {\r\n return typeof src === \"string\"\r\n ? cache$1[src] || (cache$1[src] = parse$1(src))\r\n : isPitch(src)\r\n ? interval(pitchName$1(src))\r\n : isNamed(src)\r\n ? interval(src.name)\r\n : NoInterval;\r\n}\r\nconst SIZES = [0, 2, 4, 5, 7, 9, 11];\r\nconst TYPES = \"PMMPPMM\";\r\nfunction parse$1(str) {\r\n const tokens = tokenizeInterval(str);\r\n if (tokens[0] === \"\") {\r\n return NoInterval;\r\n }\r\n const num = +tokens[0];\r\n const q = tokens[1];\r\n const step = (Math.abs(num) - 1) % 7;\r\n const t = TYPES[step];\r\n if (t === \"M\" && q === \"P\") {\r\n return NoInterval;\r\n }\r\n const type = t === \"M\" ? \"majorable\" : \"perfectable\";\r\n const name = \"\" + num + q;\r\n const dir = num < 0 ? -1 : 1;\r\n const simple = num === 8 || num === -8 ? num : dir * (step + 1);\r\n const alt = qToAlt(type, q);\r\n const oct = Math.floor((Math.abs(num) - 1) / 7);\r\n const semitones = dir * (SIZES[step] + alt + 12 * oct);\r\n const chroma = (((dir * (SIZES[step] + alt)) % 12) + 12) % 12;\r\n const coord = encode({ step, alt, oct, dir });\r\n return {\r\n empty: false,\r\n name,\r\n num,\r\n q,\r\n step,\r\n alt,\r\n dir,\r\n type,\r\n simple,\r\n semitones,\r\n chroma,\r\n coord,\r\n oct\r\n };\r\n}\r\n/**\r\n * @private\r\n */\r\nfunction coordToInterval(coord) {\r\n const [f, o = 0] = coord;\r\n const isDescending = f * 7 + o * 12 < 0;\r\n const ivl = isDescending ? [-f, -o, -1] : [f, o, 1];\r\n return interval(decode(ivl));\r\n}\r\nfunction qToAlt(type, q) {\r\n return (q === \"M\" && type === \"majorable\") ||\r\n (q === \"P\" && type === \"perfectable\")\r\n ? 0\r\n : q === \"m\" && type === \"majorable\"\r\n ? -1\r\n : /^A+$/.test(q)\r\n ? q.length\r\n : /^d+$/.test(q)\r\n ? -1 * (type === \"perfectable\" ? q.length : q.length + 1)\r\n : 0;\r\n}\r\n// return the interval name of a pitch\r\nfunction pitchName$1(props) {\r\n const { step, alt, oct = 0, dir } = props;\r\n if (!dir) {\r\n return \"\";\r\n }\r\n const num = step + 1 + 7 * oct;\r\n const d = dir < 0 ? \"-\" : \"\";\r\n const type = TYPES[step] === \"M\" ? \"majorable\" : \"perfectable\";\r\n const name = d + num + altToQ(type, alt);\r\n return name;\r\n}\r\nfunction altToQ(type, alt) {\r\n if (alt === 0) {\r\n return type === \"majorable\" ? \"M\" : \"P\";\r\n }\r\n else if (alt === -1 && type === \"majorable\") {\r\n return \"m\";\r\n }\r\n else if (alt > 0) {\r\n return fillStr(\"A\", alt);\r\n }\r\n else {\r\n return fillStr(\"d\", type === \"perfectable\" ? alt : alt + 1);\r\n }\r\n}\n\n/**\r\n * Transpose a note by an interval.\r\n *\r\n * @param {string} note - the note or note name\r\n * @param {string} interval - the interval or interval name\r\n * @return {string} the transposed note name or empty string if not valid notes\r\n * @example\r\n * import { tranpose } from \"@tonaljs/core\"\r\n * transpose(\"d3\", \"3M\") // => \"F#3\"\r\n * transpose(\"D\", \"3M\") // => \"F#\"\r\n * [\"C\", \"D\", \"E\", \"F\", \"G\"].map(pc => transpose(pc, \"M3)) // => [\"E\", \"F#\", \"G#\", \"A\", \"B\"]\r\n */\r\nfunction transpose(noteName, intervalName) {\r\n const note$1 = note(noteName);\r\n const interval$1 = interval(intervalName);\r\n if (note$1.empty || interval$1.empty) {\r\n return \"\";\r\n }\r\n const noteCoord = note$1.coord;\r\n const intervalCoord = interval$1.coord;\r\n const tr = noteCoord.length === 1\r\n ? [noteCoord[0] + intervalCoord[0]]\r\n : [noteCoord[0] + intervalCoord[0], noteCoord[1] + intervalCoord[1]];\r\n return coordToNote(tr).name;\r\n}\r\n/**\r\n * Find the interval distance between two notes or coord classes.\r\n *\r\n * To find distance between coord classes, both notes must be coord classes and\r\n * the interval is always ascending\r\n *\r\n * @param {Note|string} from - the note or note name to calculate distance from\r\n * @param {Note|string} to - the note or note name to calculate distance to\r\n * @return {string} the interval name or empty string if not valid notes\r\n *\r\n */\r\nfunction distance(fromNote, toNote) {\r\n const from = note(fromNote);\r\n const to = note(toNote);\r\n if (from.empty || to.empty) {\r\n return \"\";\r\n }\r\n const fcoord = from.coord;\r\n const tcoord = to.coord;\r\n const fifths = tcoord[0] - fcoord[0];\r\n const octs = fcoord.length === 2 && tcoord.length === 2\r\n ? tcoord[1] - fcoord[1]\r\n : -Math.floor((fifths * 7) / 12);\r\n return coordToInterval([fifths, octs]).name;\r\n}\n\nexport { accToAlt, altToAcc, coordToInterval, coordToNote, decode, deprecate, distance, encode, fillStr, interval, isNamed, isPitch, note, stepToLetter, tokenizeInterval, tokenizeNote, transpose };\n//# sourceMappingURL=index.esnext.js.map\n","import { note, transpose as transpose$1, distance as distance$1 } from '@tonaljs/core';\n\nconst fillStr = (character, times) => Array(times + 1).join(character);\r\nconst REGEX = /^(_{1,}|=|\\^{1,}|)([abcdefgABCDEFG])([,']*)$/;\r\nfunction tokenize(str) {\r\n const m = REGEX.exec(str);\r\n if (!m) {\r\n return [\"\", \"\", \"\"];\r\n }\r\n return [m[1], m[2], m[3]];\r\n}\r\n/**\r\n * Convert a (string) note in ABC notation into a (string) note in scientific notation\r\n *\r\n * @example\r\n * abcToScientificNotation(\"c\") // => \"C5\"\r\n */\r\nfunction abcToScientificNotation(str) {\r\n const [acc, letter, oct] = tokenize(str);\r\n if (letter === \"\") {\r\n return \"\";\r\n }\r\n let o = 4;\r\n for (let i = 0; i < oct.length; i++) {\r\n o += oct.charAt(i) === \",\" ? -1 : 1;\r\n }\r\n const a = acc[0] === \"_\"\r\n ? acc.replace(/_/g, \"b\")\r\n : acc[0] === \"^\"\r\n ? acc.replace(/\\^/g, \"#\")\r\n : \"\";\r\n return letter.charCodeAt(0) > 96\r\n ? letter.toUpperCase() + a + (o + 1)\r\n : letter + a + o;\r\n}\r\n/**\r\n * Convert a (string) note in scientific notation into a (string) note in ABC notation\r\n *\r\n * @example\r\n * scientificToAbcNotation(\"C#4\") // => \"^C\"\r\n */\r\nfunction scientificToAbcNotation(str) {\r\n const n = note(str);\r\n if (n.empty || !n.oct) {\r\n return \"\";\r\n }\r\n const { letter, acc, oct } = n;\r\n const a = acc[0] === \"b\" ? acc.replace(/b/g, \"_\") : acc.replace(/#/g, \"^\");\r\n const l = oct > 4 ? letter.toLowerCase() : letter;\r\n const o = oct === 5 ? \"\" : oct > 4 ? fillStr(\"'\", oct - 5) : fillStr(\",\", 4 - oct);\r\n return a + l + o;\r\n}\r\nfunction transpose(note, interval) {\r\n return scientificToAbcNotation(transpose$1(abcToScientificNotation(note), interval));\r\n}\r\nfunction distance(from, to) {\r\n return distance$1(abcToScientificNotation(from), abcToScientificNotation(to));\r\n}\r\nvar index = {\r\n abcToScientificNotation,\r\n scientificToAbcNotation,\r\n tokenize,\r\n transpose,\r\n distance\r\n};\n\nexport default index;\nexport { abcToScientificNotation, distance, scientificToAbcNotation, tokenize, transpose };\n//# sourceMappingURL=index.esnext.js.map\n","import { note } from '@tonaljs/core';\n\n// ascending range\r\nfunction ascR(b, n) {\r\n const a = [];\r\n // tslint:disable-next-line:curly\r\n for (; n--; a[n] = n + b)\r\n ;\r\n return a;\r\n}\r\n// descending range\r\nfunction descR(b, n) {\r\n const a = [];\r\n // tslint:disable-next-line:curly\r\n for (; n--; a[n] = b - n)\r\n ;\r\n return a;\r\n}\r\n/**\r\n * Creates a numeric range\r\n *\r\n * @param {number} from\r\n * @param {number} to\r\n * @return {Array}\r\n *\r\n * @example\r\n * range(-2, 2) // => [-2, -1, 0, 1, 2]\r\n * range(2, -2) // => [2, 1, 0, -1, -2]\r\n */\r\nfunction range(from, to) {\r\n return from < to ? ascR(from, to - from + 1) : descR(from, from - to + 1);\r\n}\r\n/**\r\n * Rotates a list a number of times. It\"s completly agnostic about the\r\n * contents of the list.\r\n *\r\n * @param {Integer} times - the number of rotations\r\n * @param {Array} array\r\n * @return {Array} the rotated array\r\n *\r\n * @example\r\n * rotate(1, [1, 2, 3]) // => [2, 3, 1]\r\n */\r\nfunction rotate(times, arr) {\r\n const len = arr.length;\r\n const n = ((times % len) + len) % len;\r\n return arr.slice(n, len).concat(arr.slice(0, n));\r\n}\r\n/**\r\n * Return a copy of the array with the null values removed\r\n * @function\r\n * @param {Array} array\r\n * @return {Array}\r\n *\r\n * @example\r\n * compact([\"a\", \"b\", null, \"c\"]) // => [\"a\", \"b\", \"c\"]\r\n */\r\nfunction compact(arr) {\r\n return arr.filter(n => n === 0 || n);\r\n}\r\n/**\r\n * Sort an array of notes in ascending order. Pitch classes are listed\r\n * before notes. Any string that is not a note is removed.\r\n *\r\n * @param {string[]} notes\r\n * @return {string[]} sorted array of notes\r\n *\r\n * @example\r\n * sortedNoteNames(['c2', 'c5', 'c1', 'c0', 'c6', 'c'])\r\n * // => ['C', 'C0', 'C1', 'C2', 'C5', 'C6']\r\n * sortedNoteNames(['c', 'F', 'G', 'a', 'b', 'h', 'J'])\r\n * // => ['C', 'F', 'G', 'A', 'B']\r\n */\r\nfunction sortedNoteNames(notes) {\r\n const valid = notes.map(n => note(n)).filter(n => !n.empty);\r\n return valid.sort((a, b) => a.height - b.height).map(n => n.name);\r\n}\r\n/**\r\n * Get sorted notes with duplicates removed. Pitch classes are listed\r\n * before notes.\r\n *\r\n * @function\r\n * @param {string[]} array\r\n * @return {string[]} unique sorted notes\r\n *\r\n * @example\r\n * Array.sortedUniqNoteNames(['a', 'b', 'c2', '1p', 'p2', 'c2', 'b', 'c', 'c3' ])\r\n * // => [ 'C', 'A', 'B', 'C2', 'C3' ]\r\n */\r\nfunction sortedUniqNoteNames(arr) {\r\n return sortedNoteNames(arr).filter((n, i, a) => i === 0 || n !== a[i - 1]);\r\n}\r\n/**\r\n * Randomizes the order of the specified array in-place, using the Fisher–Yates shuffle.\r\n *\r\n * @function\r\n * @param {Array} array\r\n * @return {Array} the array shuffled\r\n *\r\n * @example\r\n * shuffle([\"C\", \"D\", \"E\", \"F\"]) // => [...]\r\n */\r\nfunction shuffle(arr, rnd = Math.random) {\r\n let i;\r\n let t;\r\n let m = arr.length;\r\n while (m) {\r\n i = Math.floor(rnd() * m--);\r\n t = arr[m];\r\n arr[m] = arr[i];\r\n arr[i] = t;\r\n }\r\n return arr;\r\n}\r\n/**\r\n * Get all permutations of an array\r\n *\r\n * @param {Array} array - the array\r\n * @return {Array} an array with all the permutations\r\n * @example\r\n * permutations([\"a\", \"b\", \"c\"])) // =>\r\n * [\r\n * [\"a\", \"b\", \"c\"],\r\n * [\"b\", \"a\", \"c\"],\r\n * [\"b\", \"c\", \"a\"],\r\n * [\"a\", \"c\", \"b\"],\r\n * [\"c\", \"a\", \"b\"],\r\n * [\"c\", \"b\", \"a\"]\r\n * ]\r\n */\r\nfunction permutations(arr) {\r\n if (arr.length === 0) {\r\n return [[]];\r\n }\r\n return permutations(arr.slice(1)).reduce((acc, perm) => {\r\n return acc.concat(arr.map((e, pos) => {\r\n const newPerm = perm.slice();\r\n newPerm.splice(pos, 0, arr[0]);\r\n return newPerm;\r\n }));\r\n }, []);\r\n}\n\nexport { compact, permutations, range, rotate, shuffle, sortedNoteNames, sortedUniqNoteNames };\n//# sourceMappingURL=index.esnext.js.map\n","// ascending range\r\nfunction ascR(b, n) {\r\n const a = [];\r\n // tslint:disable-next-line:curly\r\n for (; n--; a[n] = n + b)\r\n ;\r\n return a;\r\n}\r\n// descending range\r\nfunction descR(b, n) {\r\n const a = [];\r\n // tslint:disable-next-line:curly\r\n for (; n--; a[n] = b - n)\r\n ;\r\n return a;\r\n}\r\n/**\r\n * Creates a numeric range\r\n *\r\n * @param {number} from\r\n * @param {number} to\r\n * @return {Array}\r\n *\r\n * @example\r\n * range(-2, 2) // => [-2, -1, 0, 1, 2]\r\n * range(2, -2) // => [2, 1, 0, -1, -2]\r\n */\r\nfunction range(from, to) {\r\n return from < to ? ascR(from, to - from + 1) : descR(from, from - to + 1);\r\n}\r\n/**\r\n * Rotates a list a number of times. It\"s completly agnostic about the\r\n * contents of the list.\r\n *\r\n * @param {Integer} times - the number of rotations\r\n * @param {Array} collection\r\n * @return {Array} the rotated collection\r\n *\r\n * @example\r\n * rotate(1, [1, 2, 3]) // => [2, 3, 1]\r\n */\r\nfunction rotate(times, arr) {\r\n const len = arr.length;\r\n const n = ((times % len) + len) % len;\r\n return arr.slice(n, len).concat(arr.slice(0, n));\r\n}\r\n/**\r\n * Return a copy of the collection with the null values removed\r\n * @function\r\n * @param {Array} collection\r\n * @return {Array}\r\n *\r\n * @example\r\n * compact([\"a\", \"b\", null, \"c\"]) // => [\"a\", \"b\", \"c\"]\r\n */\r\nfunction compact(arr) {\r\n return arr.filter(n => n === 0 || n);\r\n}\r\n/**\r\n * Randomizes the order of the specified collection in-place, using the Fisher–Yates shuffle.\r\n *\r\n * @function\r\n * @param {Array} collection\r\n * @return {Array} the collection shuffled\r\n *\r\n * @example\r\n * shuffle([\"C\", \"D\", \"E\", \"F\"]) // => [...]\r\n */\r\nfunction shuffle(arr, rnd = Math.random) {\r\n let i;\r\n let t;\r\n let m = arr.length;\r\n while (m) {\r\n i = Math.floor(rnd() * m--);\r\n t = arr[m];\r\n arr[m] = arr[i];\r\n arr[i] = t;\r\n }\r\n return arr;\r\n}\r\n/**\r\n * Get all permutations of an collection\r\n *\r\n * @param {Array} collection - the collection\r\n * @return {Array} an collection with all the permutations\r\n * @example\r\n * permutations([\"a\", \"b\", \"c\"])) // =>\r\n * [\r\n * [\"a\", \"b\", \"c\"],\r\n * [\"b\", \"a\", \"c\"],\r\n * [\"b\", \"c\", \"a\"],\r\n * [\"a\", \"c\", \"b\"],\r\n * [\"c\", \"a\", \"b\"],\r\n * [\"c\", \"b\", \"a\"]\r\n * ]\r\n */\r\nfunction permutations(arr) {\r\n if (arr.length === 0) {\r\n return [[]];\r\n }\r\n return permutations(arr.slice(1)).reduce((acc, perm) => {\r\n return acc.concat(arr.map((e, pos) => {\r\n const newPerm = perm.slice();\r\n newPerm.splice(pos, 0, arr[0]);\r\n return newPerm;\r\n }));\r\n }, []);\r\n}\r\nvar index = {\r\n compact,\r\n permutations,\r\n range,\r\n rotate,\r\n shuffle\r\n};\n\nexport default index;\nexport { compact, permutations, range, rotate, shuffle };\n//# sourceMappingURL=index.esnext.js.map\n","import { range, compact, rotate } from '@tonaljs/collection';\nimport { deprecate, note, interval } from '@tonaljs/core';\n\nconst EmptyPcset = {\r\n empty: true,\r\n name: \"\",\r\n setNum: 0,\r\n chroma: \"000000000000\",\r\n normalized: \"000000000000\",\r\n intervals: []\r\n};\r\n// UTILITIES\r\nconst setNumToChroma = (num) => Number(num).toString(2);\r\nconst chromaToNumber = (chroma) => parseInt(chroma, 2);\r\nconst REGEX = /^[01]{12}$/;\r\nfunction isChroma(set) {\r\n return REGEX.test(set);\r\n}\r\nconst isPcsetNum = (set) => typeof set === \"number\" && set >= 0 && set <= 4095;\r\nconst isPcset = (set) => set && isChroma(set.chroma);\r\nconst cache = { [EmptyPcset.chroma]: EmptyPcset };\r\n/**\r\n * Get the pitch class set of a collection of notes or set number or chroma\r\n */\r\nfunction get(src) {\r\n const chroma = isChroma(src)\r\n ? src\r\n : isPcsetNum(src)\r\n ? setNumToChroma(src)\r\n : Array.isArray(src)\r\n ? listToChroma(src)\r\n : isPcset(src)\r\n ? src.chroma\r\n : EmptyPcset.chroma;\r\n return (cache[chroma] = cache[chroma] || chromaToPcset(chroma));\r\n}\r\n/**\r\n * Use Pcset.properties\r\n * @function\r\n * @deprecated\r\n */\r\nconst pcset = deprecate(\"Pcset.pcset\", \"Pcset.get\", get);\r\n/**\r\n * Get pitch class set chroma\r\n * @function\r\n * @example\r\n * Pcset.chroma([\"c\", \"d\", \"e\"]); //=> \"101010000000\"\r\n */\r\nconst chroma = (set) => get(set).chroma;\r\n/**\r\n * Get intervals (from C) of a set\r\n * @function\r\n * @example\r\n * Pcset.intervals([\"c\", \"d\", \"e\"]); //=>\r\n */\r\nconst intervals = (set) => get(set).intervals;\r\n/**\r\n * Get pitch class set number\r\n * @function\r\n * @example\r\n * Pcset.num([\"c\", \"d\", \"e\"]); //=> 2192\r\n */\r\nconst num = (set) => get(set).setNum;\r\nconst IVLS = [\r\n \"1P\",\r\n \"2m\",\r\n \"2M\",\r\n \"3m\",\r\n \"3M\",\r\n \"4P\",\r\n \"5d\",\r\n \"5P\",\r\n \"6m\",\r\n \"6M\",\r\n \"7m\",\r\n \"7M\"\r\n];\r\n/**\r\n * @private\r\n * Get the intervals of a pcset *starting from C*\r\n * @param {Set} set - the pitch class set\r\n * @return {IntervalName[]} an array of interval names or an empty array\r\n * if not a valid pitch class set\r\n */\r\nfunction chromaToIntervals(chroma) {\r\n const intervals = [];\r\n for (let i = 0; i < 12; i++) {\r\n // tslint:disable-next-line:curly\r\n if (chroma.charAt(i) === \"1\")\r\n intervals.push(IVLS[i]);\r\n }\r\n return intervals;\r\n}\r\n/**\r\n * Get a list of all possible pitch class sets (all possible chromas) *having\r\n * C as root*. There are 2048 different chromas. If you want them with another\r\n * note you have to transpose it\r\n *\r\n * @see http://allthescales.org/\r\n * @return {Array} an array of possible chromas from '10000000000' to '11111111111'\r\n */\r\nfunction chromas() {\r\n return range(2048, 4095).map(setNumToChroma);\r\n}\r\n/**\r\n * Given a a list of notes or a pcset chroma, produce the rotations\r\n * of the chroma discarding the ones that starts with \"0\"\r\n *\r\n * This is used, for example, to get all the modes of a scale.\r\n *\r\n * @param {Array|string} set - the list of notes or pitchChr of the set\r\n * @param {boolean} normalize - (Optional, true by default) remove all\r\n * the rotations that starts with \"0\"\r\n * @return {Array} an array with all the modes of the chroma\r\n *\r\n * @example\r\n * Pcset.modes([\"C\", \"D\", \"E\"]).map(Pcset.intervals)\r\n */\r\nfunction modes(set, normalize = true) {\r\n const pcs = get(set);\r\n const binary = pcs.chroma.split(\"\");\r\n return compact(binary.map((_, i) => {\r\n const r = rotate(i, binary);\r\n return normalize && r[0] === \"0\" ? null : r.join(\"\");\r\n }));\r\n}\r\n/**\r\n * Test if two pitch class sets are numentical\r\n *\r\n * @param {Array|string} set1 - one of the pitch class sets\r\n * @param {Array|string} set2 - the other pitch class set\r\n * @return {boolean} true if they are equal\r\n * @example\r\n * Pcset.isEqual([\"c2\", \"d3\"], [\"c5\", \"d2\"]) // => true\r\n */\r\nfunction isEqual(s1, s2) {\r\n return get(s1).setNum === get(s2).setNum;\r\n}\r\n/**\r\n * Create a function that test if a collection of notes is a\r\n * subset of a given set\r\n *\r\n * The function is curryfied.\r\n *\r\n * @param {PcsetChroma|NoteName[]} set - the superset to test against (chroma or\r\n * list of notes)\r\n * @return{function(PcsetChroma|NoteNames[]): boolean} a function accepting a set\r\n * to test against (chroma or list of notes)\r\n * @example\r\n * const inCMajor = Pcset.isSubsetOf([\"C\", \"E\", \"G\"])\r\n * inCMajor([\"e6\", \"c4\"]) // => true\r\n * inCMajor([\"e6\", \"c4\", \"d3\"]) // => false\r\n */\r\nfunction isSubsetOf(set) {\r\n const s = get(set).setNum;\r\n return (notes) => {\r\n const o = get(notes).setNum;\r\n // tslint:disable-next-line: no-bitwise\r\n return s && s !== o && (o & s) === o;\r\n };\r\n}\r\n/**\r\n * Create a function that test if a collection of notes is a\r\n * superset of a given set (it contains all notes and at least one more)\r\n *\r\n * @param {Set} set - an array of notes or a chroma set string to test against\r\n * @return {(subset: Set): boolean} a function that given a set\r\n * returns true if is a subset of the first one\r\n * @example\r\n * const extendsCMajor = Pcset.isSupersetOf([\"C\", \"E\", \"G\"])\r\n * extendsCMajor([\"e6\", \"a\", \"c4\", \"g2\"]) // => true\r\n * extendsCMajor([\"c6\", \"e4\", \"g3\"]) // => false\r\n */\r\nfunction isSupersetOf(set) {\r\n const s = get(set).setNum;\r\n return (notes) => {\r\n const o = get(notes).setNum;\r\n // tslint:disable-next-line: no-bitwise\r\n return s && s !== o && (o | s) === o;\r\n };\r\n}\r\n/**\r\n * Test if a given pitch class set includes a note\r\n *\r\n * @param {Array} set - the base set to test against\r\n * @param {string} note - the note to test\r\n * @return {boolean} true if the note is included in the pcset\r\n *\r\n * Can be partially applied\r\n *\r\n * @example\r\n * const isNoteInCMajor = isNoteIncludedIn(['C', 'E', 'G'])\r\n * isNoteInCMajor('C4') // => true\r\n * isNoteInCMajor('C#4') // => false\r\n */\r\nfunction isNoteIncludedIn(set) {\r\n const s = get(set);\r\n return (noteName) => {\r\n const n = note(noteName);\r\n return s && !n.empty && s.chroma.charAt(n.chroma) === \"1\";\r\n };\r\n}\r\n/** @deprecated use: isNoteIncludedIn */\r\nconst includes = isNoteIncludedIn;\r\n/**\r\n * Filter a list with a pitch class set\r\n *\r\n * @param {Array|string} set - the pitch class set notes\r\n * @param {Array|string} notes - the note list to be filtered\r\n * @return {Array} the filtered notes\r\n *\r\n * @example\r\n * Pcset.filter([\"C\", \"D\", \"E\"], [\"c2\", \"c#2\", \"d2\", \"c3\", \"c#3\", \"d3\"]) // => [ \"c2\", \"d2\", \"c3\", \"d3\" ])\r\n * Pcset.filter([\"C2\"], [\"c2\", \"c#2\", \"d2\", \"c3\", \"c#3\", \"d3\"]) // => [ \"c2\", \"c3\" ])\r\n */\r\nfunction filter(set) {\r\n const isIncluded = isNoteIncludedIn(set);\r\n return (notes) => {\r\n return notes.filter(isIncluded);\r\n };\r\n}\r\nvar index = {\r\n get,\r\n chroma,\r\n num,\r\n intervals,\r\n chromas,\r\n isSupersetOf,\r\n isSubsetOf,\r\n isNoteIncludedIn,\r\n isEqual,\r\n filter,\r\n modes,\r\n // deprecated\r\n pcset\r\n};\r\n//// PRIVATE ////\r\nfunction chromaRotations(chroma) {\r\n const binary = chroma.split(\"\");\r\n return binary.map((_, i) => rotate(i, binary).join(\"\"));\r\n}\r\nfunction chromaToPcset(chroma) {\r\n const setNum = chromaToNumber(chroma);\r\n const normalizedNum = chromaRotations(chroma)\r\n .map(chromaToNumber)\r\n .filter(n => n >= 2048)\r\n .sort()[0];\r\n const normalized = setNumToChroma(normalizedNum);\r\n const intervals = chromaToIntervals(chroma);\r\n return {\r\n empty: false,\r\n name: \"\",\r\n setNum,\r\n chroma,\r\n normalized,\r\n intervals\r\n };\r\n}\r\nfunction listToChroma(set) {\r\n if (set.length === 0) {\r\n return EmptyPcset.chroma;\r\n }\r\n let pitch;\r\n const binary = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\r\n // tslint:disable-next-line:prefer-for-of\r\n for (let i = 0; i < set.length; i++) {\r\n pitch = note(set[i]);\r\n // tslint:disable-next-line: curly\r\n if (pitch.empty)\r\n pitch = interval(set[i]);\r\n // tslint:disable-next-line: curly\r\n if (!pitch.empty)\r\n binary[pitch.chroma] = 1;\r\n }\r\n return binary.join(\"\");\r\n}\n\nexport default index;\nexport { EmptyPcset, chromaToIntervals, chromas, filter, get, includes, isEqual, isNoteIncludedIn, isSubsetOf, isSupersetOf, modes, pcset };\n//# sourceMappingURL=index.esnext.js.map\n","import { deprecate } from '@tonaljs/core';\nimport { get as get$1, EmptyPcset } from '@tonaljs/pcset';\n\n/**\r\n * @private\r\n * Chord List\r\n * Source: https://en.wikibooks.org/wiki/Music_Theory/Complete_List_of_Chord_Patterns\r\n * Format: [\"intervals\", \"full name\", \"abrv1 abrv2\"]\r\n */\r\nconst CHORDS = [\r\n // ==Major==\r\n [\"1P 3M 5P\", \"major\", \"M \"],\r\n [\"1P 3M 5P 7M\", \"major seventh\", \"maj7 Δ ma7 M7 Maj7\"],\r\n [\"1P 3M 5P 7M 9M\", \"major ninth\", \"maj9 Δ9\"],\r\n [\"1P 3M 5P 7M 9M 13M\", \"major thirteenth\", \"maj13 Maj13\"],\r\n [\"1P 3M 5P 6M\", \"sixth\", \"6 add6 add13 M6\"],\r\n [\"1P 3M 5P 6M 9M\", \"sixth/ninth\", \"6/9 69\"],\r\n [\"1P 3M 5P 7M 11A\", \"lydian\", \"maj#4 Δ#4 Δ#11\"],\r\n [\"1P 3M 6m 7M\", \"major seventh b6\", \"M7b6\"],\r\n // ==Minor==\r\n // '''Normal'''\r\n [\"1P 3m 5P\", \"minor\", \"m min -\"],\r\n [\"1P 3m 5P 7m\", \"minor seventh\", \"m7 min7 mi7 -7\"],\r\n [\"1P 3m 5P 7M\", \"minor/major seventh\", \"m/ma7 m/maj7 mM7 m/M7 -Δ7 mΔ\"],\r\n [\"1P 3m 5P 6M\", \"minor sixth\", \"m6\"],\r\n [\"1P 3m 5P 7m 9M\", \"minor ninth\", \"m9\"],\r\n [\"1P 3m 5P 7m 9M 11P\", \"minor eleventh\", \"m11\"],\r\n [\"1P 3m 5P 7m 9M 13M\", \"minor thirteenth\", \"m13\"],\r\n // '''Diminished'''\r\n [\"1P 3m 5d\", \"diminished\", \"dim ° o\"],\r\n [\"1P 3m 5d 7d\", \"diminished seventh\", \"dim7 °7 o7\"],\r\n [\"1P 3m 5d 7m\", \"half-diminished\", \"m7b5 ø\"],\r\n // ==Dominant/Seventh==\r\n // '''Normal'''\r\n [\"1P 3M 5P 7m\", \"dominant seventh\", \"7 dom\"],\r\n [\"1P 3M 5P 7m 9M\", \"dominant ninth\", \"9\"],\r\n [\"1P 3M 5P 7m 9M 13M\", \"dominant thirteenth\", \"13\"],\r\n [\"1P 3M 5P 7m 11A\", \"lydian dominant seventh\", \"7#11 7#4\"],\r\n // '''Altered'''\r\n [\"1P 3M 5P 7m 9m\", \"dominant b9\", \"7b9\"],\r\n [\"1P 3M 5P 7m 9A\", \"dominant #9\", \"7#9\"],\r\n [\"1P 3M 7m 9m\", \"altered\", \"alt7\"],\r\n // '''Suspended'''\r\n [\"1P 4P 5P\", \"suspended 4th\", \"sus4\"],\r\n [\"1P 2M 5P\", \"suspended 2nd\", \"sus2\"],\r\n [\"1P 4P 5P 7m\", \"suspended 4th seventh\", \"7sus4\"],\r\n [\"1P 5P 7m 9M 11P\", \"eleventh\", \"11\"],\r\n [\"1P 4P 5P 7m 9m\", \"suspended 4th b9\", \"b9sus phryg\"],\r\n // ==Other==\r\n [\"1P 5P\", \"fifth\", \"5\"],\r\n [\"1P 3M 5A\", \"augmented\", \"aug + +5\"],\r\n [\"1P 3M 5A 7M\", \"augmented seventh\", \"maj7#5 maj7+5\"],\r\n [\"1P 3M 5P 7M 9M 11A\", \"major #11 (lydian)\", \"maj9#11 Δ9#11\"],\r\n // ==Legacy==\r\n [\"1P 2M 4P 5P\", \"\", \"sus24 sus4add9\"],\r\n [\"1P 3M 13m\", \"\", \"Mb6\"],\r\n [\"1P 3M 5A 7M 9M\", \"\", \"maj9#5 Maj9#5\"],\r\n [\"1P 3M 5A 7m\", \"\", \"7#5 +7 7aug aug7\"],\r\n [\"1P 3M 5A 7m 9A\", \"\", \"7#5#9 7alt 7#5#9_ 7#9b13_\"],\r\n [\"1P 3M 5A 7m 9M\", \"\", \"9#5 9+\"],\r\n [\"1P 3M 5A 7m 9M 11A\", \"\", \"9#5#11\"],\r\n [\"1P 3M 5A 7m 9m\", \"\", \"7#5b9\"],\r\n [\"1P 3M 5A 7m 9m 11A\", \"\", \"7#5b9#11\"],\r\n [\"1P 3M 5A 9A\", \"\", \"+add#9\"],\r\n [\"1P 3M 5A 9M\", \"\", \"M#5add9 +add9\"],\r\n [\"1P 3M 5P 6M 11A\", \"\", \"M6#11 M6b5 6#11 6b5\"],\r\n [\"1P 3M 5P 6M 7M 9M\", \"\", \"M7add13\"],\r\n [\"1P 3M 5P 6M 9M 11A\", \"\", \"69#11\"],\r\n [\"1P 3M 5P 6m 7m\", \"\", \"7b6\"],\r\n [\"1P 3M 5P 7M 9A 11A\", \"\", \"maj7#9#11\"],\r\n [\"1P 3M 5P 7M 9M 11A 13M\", \"\", \"M13#11 maj13#11 M13+4 M13#4\"],\r\n [\"1P 3M 5P 7M 9m\", \"\", \"M7b9\"],\r\n [\"1P 3M 5P 7m 11A 13m\", \"\", \"7#11b13 7b5b13\"],\r\n [\"1P 3M 5P 7m 13M\", \"\", \"7add6 67 7add13\"],\r\n [\"1P 3M 5P 7m 9A 11A\", \"\", \"7#9#11 7b5#9\"],\r\n [\"1P 3M 5P 7m 9A 11A 13M\", \"\", \"13#9#11\"],\r\n [\"1P 3M 5P 7m 9A 11A 13m\", \"\", \"7#9#11b13\"],\r\n [\"1P 3M 5P 7m 9A 13M\", \"\", \"13#9 13#9_\"],\r\n [\"1P 3M 5P 7m 9A 13m\", \"\", \"7#9b13\"],\r\n [\"1P 3M 5P 7m 9M 11A\", \"\", \"9#11 9+4 9#4 9#11_ 9#4_\"],\r\n [\"1P 3M 5P 7m 9M 11A 13M\", \"\", \"13#11 13+4 13#4\"],\r\n [\"1P 3M 5P 7m 9M 11A 13m\", \"\", \"9#11b13 9b5b13\"],\r\n [\"1P 3M 5P 7m 9m 11A\", \"\", \"7b9#11 7b5b9\"],\r\n [\"1P 3M 5P 7m 9m 11A 13M\", \"\", \"13b9#11\"],\r\n [\"1P 3M 5P 7m 9m 11A 13m\", \"\", \"7b9b13#11 7b9#11b13 7b5b9b13\"],\r\n [\"1P 3M 5P 7m 9m 13M\", \"\", \"13b9\"],\r\n [\"1P 3M 5P 7m 9m 13m\", \"\", \"7b9b13\"],\r\n [\"1P 3M 5P 7m 9m 9A\", \"\", \"7b9#9\"],\r\n [\"1P 3M 5P 9M\", \"\", \"Madd9 2 add9 add2\"],\r\n [\"1P 3M 5P 9m\", \"\", \"Maddb9\"],\r\n [\"1P 3M 5d\", \"\", \"Mb5\"],\r\n [\"1P 3M 5d 6M 7m 9M\", \"\", \"13b5\"],\r\n [\"1P 3M 5d 7M\", \"\", \"M7b5\"],\r\n [\"1P 3M 5d 7M 9M\", \"\", \"M9b5\"],\r\n [\"1P 3M 5d 7m\", \"\", \"7b5\"],\r\n [\"1P 3M 5d 7m 9M\", \"\", \"9b5\"],\r\n [\"1P 3M 7m\", \"\", \"7no5\"],\r\n [\"1P 3M 7m 13m\", \"\", \"7b13\"],\r\n [\"1P 3M 7m 9M\", \"\", \"9no5\"],\r\n [\"1P 3M 7m 9M 13M\", \"\", \"13no5\"],\r\n [\"1P 3M 7m 9M 13m\", \"\", \"9b13\"],\r\n [\"1P 3m 4P 5P\", \"\", \"madd4\"],\r\n [\"1P 3m 5A\", \"\", \"m#5 m+ mb6\"],\r\n [\"1P 3m 5P 6M 9M\", \"\", \"m69 _69\"],\r\n [\"1P 3m 5P 6m 7M\", \"\", \"mMaj7b6\"],\r\n [\"1P 3m 5P 6m 7M 9M\", \"\", \"mMaj9b6\"],\r\n [\"1P 3m 5P 7M 9M\", \"\", \"mMaj9 -Maj9\"],\r\n [\"1P 3m 5P 7m 11P\", \"\", \"m7add11 m7add4\"],\r\n [\"1P 3m 5P 9M\", \"\", \"madd9\"],\r\n [\"1P 3m 5d 6M 7M\", \"\", \"o7M7\"],\r\n [\"1P 3m 5d 7M\", \"\", \"oM7\"],\r\n [\"1P 3m 6m 7M\", \"\", \"mb6M7\"],\r\n [\"1P 3m 6m 7m\", \"\", \"m7#5\"],\r\n [\"1P 3m 6m 7m 9M\", \"\", \"m9#5\"],\r\n [\"1P 3m 6m 7m 9M 11P\", \"\", \"m11A\"],\r\n [\"1P 3m 6m 9m\", \"\", \"mb6b9\"],\r\n [\"1P 3m 7m 12d 2M\", \"\", \"m9b5 h9 -9b5\"],\r\n [\"1P 3m 7m 12d 2M 4P\", \"\", \"m11b5 h11 _11b5\"],\r\n [\"1P 4P 5A 7M\", \"\", \"M7#5sus4\"],\r\n [\"1P 4P 5A 7M 9M\", \"\", \"M9#5sus4\"],\r\n [\"1P 4P 5A 7m\", \"\", \"7#5sus4\"],\r\n [\"1P 4P 5P 7M\", \"\", \"M7sus4\"],\r\n [\"1P 4P 5P 7M 9M\", \"\", \"M9sus4\"],\r\n [\"1P 4P 5P 7m 9M\", \"\", \"9sus4 9sus\"],\r\n [\"1P 4P 5P 7m 9M 13M\", \"\", \"13sus4 13sus\"],\r\n [\"1P 4P 5P 7m 9m 13m\", \"\", \"7sus4b9b13 7b9b13sus4\"],\r\n [\"1P 4P 7m 10m\", \"\", \"4 quartal\"],\r\n [\"1P 5P 7m 9m 11P\", \"\", \"11b9\"]\r\n];\n\nconst NoChordType = {\r\n ...EmptyPcset,\r\n name: \"\",\r\n quality: \"Unknown\",\r\n intervals: [],\r\n aliases: []\r\n};\r\nlet dictionary = [];\r\nlet index = {};\r\n/**\r\n * Given a chord name or chroma, return the chord properties\r\n * @param {string} source - chord name or pitch class set chroma\r\n * @example\r\n * import { get } from 'tonaljs/chord-type'\r\n * get('major') // => { name: 'major', ... }\r\n */\r\nfunction get(type) {\r\n return index[type] || NoChordType;\r\n}\r\nconst chordType = deprecate(\"ChordType.chordType\", \"ChordType.get\", get);\r\n/**\r\n * Get all chord (long) names\r\n */\r\nfunction names() {\r\n return dictionary.map(chord => chord.name).filter(x => x);\r\n}\r\n/**\r\n * Get all chord symbols\r\n */\r\nfunction symbols() {\r\n return dictionary.map(chord => chord.aliases[0]).filter(x => x);\r\n}\r\n/**\r\n * Keys used to reference chord types\r\n */\r\nfunction keys() {\r\n return Object.keys(index);\r\n}\r\n/**\r\n * Return a list of all chord types\r\n */\r\nfunction all() {\r\n return dictionary.slice();\r\n}\r\nconst entries = deprecate(\"ChordType.entries\", \"ChordType.all\", all);\r\n/**\r\n * Clear the dictionary\r\n */\r\nfunction removeAll() {\r\n dictionary = [];\r\n index = {};\r\n}\r\n/**\r\n * Add a chord to the dictionary.\r\n * @param intervals\r\n * @param aliases\r\n * @param [fullName]\r\n */\r\nfunction add(intervals, aliases, fullName) {\r\n const quality = getQuality(intervals);\r\n const chord = {\r\n ...get$1(intervals),\r\n name: fullName || \"\",\r\n quality,\r\n intervals,\r\n aliases\r\n };\r\n dictionary.push(chord);\r\n if (chord.name) {\r\n index[chord.name] = chord;\r\n }\r\n index[chord.setNum] = chord;\r\n index[chord.chroma] = chord;\r\n chord.aliases.forEach(alias => addAlias(chord, alias));\r\n}\r\nfunction addAlias(chord, alias) {\r\n index[alias] = chord;\r\n}\r\nfunction getQuality(intervals) {\r\n const has = (interval) => intervals.indexOf(interval) !== -1;\r\n return has(\"5A\")\r\n ? \"Augmented\"\r\n : has(\"3M\")\r\n ? \"Major\"\r\n : has(\"5d\")\r\n ? \"Diminished\"\r\n : has(\"3m\")\r\n ? \"Minor\"\r\n : \"Unknown\";\r\n}\r\nCHORDS.forEach(([ivls, fullName, names]) => add(ivls.split(\" \"), names.split(\" \"), fullName));\r\ndictionary.sort((a, b) => a.setNum - b.setNum);\r\nvar index$1 = {\r\n names,\r\n symbols,\r\n get,\r\n all,\r\n add,\r\n removeAll,\r\n keys,\r\n // deprecated\r\n entries,\r\n chordType\r\n};\n\nexport default index$1;\nexport { add, addAlias, all, chordType, entries, get, keys, names, removeAll, symbols };\n//# sourceMappingURL=index.esnext.js.map\n","import { get } from '@tonaljs/chord-type';\nimport { note } from '@tonaljs/core';\nimport { modes } from '@tonaljs/pcset';\n\nconst NotFound = { weight: 0, name: \"\" };\r\nconst namedSet = (notes) => {\r\n const pcToName = notes.reduce((record, n) => {\r\n const chroma = note(n).chroma;\r\n if (chroma !== undefined) {\r\n record[chroma] = record[chroma] || note(n).name;\r\n }\r\n return record;\r\n }, {});\r\n return (chroma) => pcToName[chroma];\r\n};\r\nfunction detect(source) {\r\n const notes = source.map(n => note(n).pc).filter(x => x);\r\n if (note.length === 0) {\r\n return [];\r\n }\r\n const found = findExactMatches(notes, 1);\r\n return found\r\n .filter(chord => chord.weight)\r\n .sort((a, b) => b.weight - a.weight)\r\n .map(chord => chord.name);\r\n}\r\nfunction findExactMatches(notes, weight) {\r\n const tonic = notes[0];\r\n const tonicChroma = note(tonic).chroma;\r\n const noteName = namedSet(notes);\r\n const allModes = modes(notes, false);\r\n const found = allModes.map((mode, chroma) => {\r\n const chordName = get(mode).aliases[0];\r\n if (!chordName) {\r\n return NotFound;\r\n }\r\n const baseNote = noteName(chroma);\r\n const isInversion = chroma !== tonicChroma;\r\n if (isInversion) {\r\n return { weight: 0.5 * weight, name: `${baseNote}${chordName}/${tonic}` };\r\n }\r\n else {\r\n return { weight: 1 * weight, name: `${baseNote}${chordName}` };\r\n }\r\n });\r\n return found;\r\n}\r\nvar index = { detect };\n\nexport default index;\nexport { detect };\n//# sourceMappingURL=index.esnext.js.map\n","import { deprecate } from '@tonaljs/core';\nimport { EmptyPcset, get as get$1 } from '@tonaljs/pcset';\n\n// SCALES\r\n// Format: [\"intervals\", \"name\", \"alias1\", \"alias2\", ...]\r\nconst SCALES = [\r\n // 5-note scales\r\n [\"1P 2M 3M 5P 6M\", \"major pentatonic\", \"pentatonic\"],\r\n [\"1P 3M 4P 5P 7M\", \"ionian pentatonic\"],\r\n [\"1P 3M 4P 5P 7m\", \"mixolydian pentatonic\", \"indian\"],\r\n [\"1P 2M 4P 5P 6M\", \"ritusen\"],\r\n [\"1P 2M 4P 5P 7m\", \"egyptian\"],\r\n [\"1P 3M 4P 5d 7m\", \"neopolitan major pentatonic\"],\r\n [\"1P 3m 4P 5P 6m\", \"vietnamese 1\"],\r\n [\"1P 2m 3m 5P 6m\", \"pelog\"],\r\n [\"1P 2m 4P 5P 6m\", \"kumoijoshi\"],\r\n [\"1P 2M 3m 5P 6m\", \"hirajoshi\"],\r\n [\"1P 2m 4P 5d 7m\", \"iwato\"],\r\n [\"1P 2m 4P 5P 7m\", \"in-sen\"],\r\n [\"1P 3M 4A 5P 7M\", \"lydian pentatonic\", \"chinese\"],\r\n [\"1P 3m 4P 6m 7m\", \"malkos raga\"],\r\n [\"1P 3m 4P 5d 7m\", \"locrian pentatonic\", \"minor seven flat five pentatonic\"],\r\n [\"1P 3m 4P 5P 7m\", \"minor pentatonic\", \"vietnamese 2\"],\r\n [\"1P 3m 4P 5P 6M\", \"minor six pentatonic\"],\r\n [\"1P 2M 3m 5P 6M\", \"flat three pentatonic\", \"kumoi\"],\r\n [\"1P 2M 3M 5P 6m\", \"flat six pentatonic\"],\r\n [\"1P 2m 3M 5P 6M\", \"scriabin\"],\r\n [\"1P 3M 5d 6m 7m\", \"whole tone pentatonic\"],\r\n [\"1P 3M 4A 5A 7M\", \"lydian #5P pentatonic\"],\r\n [\"1P 3M 4A 5P 7m\", \"lydian dominant pentatonic\"],\r\n [\"1P 3m 4P 5P 7M\", \"minor #7M pentatonic\"],\r\n [\"1P 3m 4d 5d 7m\", \"super locrian pentatonic\"],\r\n // 6-note scales\r\n [\"1P 2M 3m 4P 5P 7M\", \"minor hexatonic\"],\r\n [\"1P 2A 3M 5P 5A 7M\", \"augmented\"],\r\n [\"1P 3m 4P 5d 5P 7m\", \"minor blues\", \"blues\"],\r\n [\"1P 2M 3m 3M 5P 6M\", \"major blues\"],\r\n [\"1P 2M 4P 5P 6M 7m\", \"piongio\"],\r\n [\"1P 2m 3M 4A 6M 7m\", \"prometheus neopolitan\"],\r\n [\"1P 2M 3M 4A 6M 7m\", \"prometheus\"],\r\n [\"1P 2m 3M 5d 6m 7m\", \"mystery #1\"],\r\n [\"1P 2m 3M 4P 5A 6M\", \"six tone symmetric\"],\r\n [\"1P 2M 3M 4A 5A 7m\", \"whole tone\"],\r\n // 7-note scales\r\n [\"1P 2M 3M 4P 5d 6m 7m\", \"locrian major\", \"arabian\"],\r\n [\"1P 2m 3M 4A 5P 6m 7M\", \"double harmonic lydian\"],\r\n [\"1P 2M 3m 4P 5P 6m 7M\", \"harmonic minor\"],\r\n [\r\n \"1P 2m 3m 3M 5d 6m 7m\",\r\n \"altered\",\r\n \"super locrian\",\r\n \"diminished whole tone\",\r\n \"pomeroy\"\r\n ],\r\n [\"1P 2M 3m 4P 5d 6m 7m\", \"locrian #2\", \"half-diminished\", '\"aeolian b5'],\r\n [\r\n \"1P 2M 3M 4P 5P 6m 7m\",\r\n \"mixolydian b6\",\r\n \"melodic minor fifth mode\",\r\n \"hindu\"\r\n ],\r\n [\"1P 2M 3M 4A 5P 6M 7m\", \"lydian dominant\", \"lydian b7\", \"overtone\"],\r\n [\"1P 2M 3M 4A 5P 6M 7M\", \"lydian\"],\r\n [\"1P 2M 3M 4A 5A 6M 7M\", \"lydian augmented\"],\r\n [\r\n \"1P 2m 3m 4P 5P 6M 7m\",\r\n \"dorian b2\",\r\n \"phrygian #6\",\r\n \"melodic minor second mode\"\r\n ],\r\n [\"1P 2M 3m 4P 5P 6M 7M\", \"melodic minor\"],\r\n [\"1P 2m 3m 4P 5d 6m 7m\", \"locrian\"],\r\n [\r\n \"1P 2m 3m 4d 5d 6m 7d\",\r\n \"ultralocrian\",\r\n \"superlocrian bb7\",\r\n \"·superlocrian diminished\"\r\n ],\r\n [\"1P 2m 3m 4P 5d 6M 7m\", \"locrian 6\", \"locrian natural 6\", \"locrian sharp 6\"],\r\n [\"1P 2A 3M 4P 5P 5A 7M\", \"augmented heptatonic\"],\r\n [\"1P 2M 3m 5d 5P 6M 7m\", \"romanian minor\"],\r\n [\"1P 2M 3m 4A 5P 6M 7m\", \"dorian #4\"],\r\n [\"1P 2M 3m 4A 5P 6M 7M\", \"lydian diminished\"],\r\n [\"1P 2m 3m 4P 5P 6m 7m\", \"phrygian\"],\r\n [\"1P 2M 3M 4A 5A 7m 7M\", \"leading whole tone\"],\r\n [\"1P 2M 3M 4A 5P 6m 7m\", \"lydian minor\"],\r\n [\"1P 2m 3M 4P 5P 6m 7m\", \"phrygian dominant\", \"spanish\", \"phrygian major\"],\r\n [\"1P 2m 3m 4P 5P 6m 7M\", \"balinese\"],\r\n [\"1P 2m 3m 4P 5P 6M 7M\", \"neopolitan major\"],\r\n [\"1P 2M 3m 4P 5P 6m 7m\", \"aeolian\", \"minor\"],\r\n [\"1P 2M 3M 4P 5P 6m 7M\", \"harmonic major\"],\r\n [\"1P 2m 3M 4P 5P 6m 7M\", \"double harmonic major\", \"gypsy\"],\r\n [\"1P 2M 3m 4P 5P 6M 7m\", \"dorian\"],\r\n [\"1P 2M 3m 4A 5P 6m 7M\", \"hungarian minor\"],\r\n [\"1P 2A 3M 4A 5P 6M 7m\", \"hungarian major\"],\r\n [\"1P 2m 3M 4P 5d 6M 7m\", \"oriental\"],\r\n [\"1P 2m 3m 3M 4A 5P 7m\", \"flamenco\"],\r\n [\"1P 2m 3m 4A 5P 6m 7M\", \"todi raga\"],\r\n [\"1P 2M 3M 4P 5P 6M 7m\", \"mixolydian\", \"dominant\"],\r\n [\"1P 2m 3M 4P 5d 6m 7M\", \"persian\"],\r\n [\"1P 2M 3M 4P 5P 6M 7M\", \"major\", \"ionian\"],\r\n [\"1P 2m 3M 5d 6m 7m 7M\", \"enigmatic\"],\r\n [\r\n \"1P 2M 3M 4P 5A 6M 7M\",\r\n \"major augmented\",\r\n \"major #5\",\r\n \"ionian augmented\",\r\n \"ionian #5\"\r\n ],\r\n [\"1P 2A 3M 4A 5P 6M 7M\", \"lydian #9\"],\r\n // 8-note scales\r\n [\"1P 2m 3M 4P 4A 5P 6m 7M\", \"purvi raga\"],\r\n [\"1P 2m 3m 3M 4P 5P 6m 7m\", \"spanish heptatonic\"],\r\n [\"1P 2M 3M 4P 5P 6M 7m 7M\", \"bebop\"],\r\n [\"1P 2M 3m 3M 4P 5P 6M 7m\", \"bebop minor\"],\r\n [\"1P 2M 3M 4P 5P 5A 6M 7M\", \"bebop major\"],\r\n [\"1P 2m 3m 4P 5d 5P 6m 7m\", \"bebop locrian\"],\r\n [\"1P 2M 3m 4P 5P 6m 7m 7M\", \"minor bebop\"],\r\n [\"1P 2M 3m 4P 5d 6m 6M 7M\", \"diminished\", \"whole-half diminished\"],\r\n [\"1P 2M 3M 4P 5d 5P 6M 7M\", \"ichikosucho\"],\r\n [\"1P 2M 3m 4P 5P 6m 6M 7M\", \"minor six diminished\"],\r\n [\"1P 2m 3m 3M 4A 5P 6M 7m\", \"half-whole diminished\", \"dominant diminished\"],\r\n [\"1P 3m 3M 4P 5P 6M 7m 7M\", \"kafi raga\"],\r\n // 9-note scales\r\n [\"1P 2M 3m 3M 4P 5d 5P 6M 7m\", \"composite blues\"],\r\n // 12-note scales\r\n [\"1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M\", \"chromatic\"]\r\n];\n\nconst NoScaleType = {\r\n ...EmptyPcset,\r\n intervals: [],\r\n aliases: []\r\n};\r\nlet dictionary = [];\r\nlet index = {};\r\nfunction names() {\r\n return dictionary.map(scale => scale.name);\r\n}\r\n/**\r\n * Given a scale name or chroma, return the scale properties\r\n *\r\n * @param {string} type - scale name or pitch class set chroma\r\n * @example\r\n * import { get } from 'tonaljs/scale-type'\r\n * get('major') // => { name: 'major', ... }\r\n */\r\nfunction get(type) {\r\n return index[type] || NoScaleType;\r\n}\r\nconst scaleType = deprecate(\"ScaleDictionary.scaleType\", \"ScaleType.get\", get);\r\n/**\r\n * Return a list of all scale types\r\n */\r\nfunction all() {\r\n return dictionary.slice();\r\n}\r\nconst entries = deprecate(\"ScaleDictionary.entries\", \"ScaleType.all\", all);\r\n/**\r\n * Keys used to reference scale types\r\n */\r\nfunction keys() {\r\n return Object.keys(index);\r\n}\r\n/**\r\n * Clear the dictionary\r\n */\r\nfunction removeAll() {\r\n dictionary = [];\r\n index = {};\r\n}\r\n/**\r\n * Add a scale into dictionary\r\n * @param intervals\r\n * @param name\r\n * @param aliases\r\n */\r\nfunction add(intervals, name, aliases = []) {\r\n const scale = { ...get$1(intervals), name, intervals, aliases };\r\n dictionary.push(scale);\r\n index[scale.name] = scale;\r\n index[scale.setNum] = scale;\r\n index[scale.chroma] = scale;\r\n scale.aliases.forEach(alias => addAlias(scale, alias));\r\n return scale;\r\n}\r\nfunction addAlias(scale, alias) {\r\n index[alias] = scale;\r\n}\r\nSCALES.forEach(([ivls, name, ...aliases]) => add(ivls.split(\" \"), name, aliases));\r\nvar index$1 = {\r\n names,\r\n get,\r\n all,\r\n add,\r\n removeAll,\r\n keys,\r\n // deprecated\r\n entries,\r\n scaleType\r\n};\n\nexport default index$1;\nexport { NoScaleType, add, addAlias, all, entries, get, keys, names, removeAll, scaleType };\n//# sourceMappingURL=index.esnext.js.map\n","import { detect } from '@tonaljs/chord-detect';\nexport { detect } from '@tonaljs/chord-detect';\nimport { get as get$1, all as all$1 } from '@tonaljs/chord-type';\nimport { tokenizeNote, transpose as transpose$1, deprecate, note } from '@tonaljs/core';\nimport { isSupersetOf, isSubsetOf } from '@tonaljs/pcset';\nimport { all } from '@tonaljs/scale-type';\n\nconst NoChord = {\r\n empty: true,\r\n name: \"\",\r\n type: \"\",\r\n tonic: null,\r\n setNum: NaN,\r\n quality: \"Unknown\",\r\n chroma: \"\",\r\n normalized: \"\",\r\n aliases: [],\r\n notes: [],\r\n intervals: []\r\n};\r\n// 6, 64, 7, 9, 11 and 13 are consider part of the chord\r\n// (see https://github.com/danigb/tonal/issues/55)\r\nconst NUM_TYPES = /^(6|64|7|9|11|13)$/;\r\n/**\r\n * Tokenize a chord name. It returns an array with the tonic and chord type\r\n * If not tonic is found, all the name is considered the chord name.\r\n *\r\n * This function does NOT check if the chord type exists or not. It only tries\r\n * to split the tonic and chord type.\r\n *\r\n * @function\r\n * @param {string} name - the chord name\r\n * @return {Array} an array with [tonic, type]\r\n * @example\r\n * tokenize(\"Cmaj7\") // => [ \"C\", \"maj7\" ]\r\n * tokenize(\"C7\") // => [ \"C\", \"7\" ]\r\n * tokenize(\"mMaj7\") // => [ null, \"mMaj7\" ]\r\n * tokenize(\"Cnonsense\") // => [ null, \"nonsense\" ]\r\n */\r\nfunction tokenize(name) {\r\n const [letter, acc, oct, type] = tokenizeNote(name);\r\n if (letter === \"\") {\r\n return [\"\", name];\r\n }\r\n // aug is augmented (see https://github.com/danigb/tonal/issues/55)\r\n if (letter === \"A\" && type === \"ug\") {\r\n return [\"\", \"aug\"];\r\n }\r\n // see: https://github.com/tonaljs/tonal/issues/70\r\n if (!type && (oct === \"4\" || oct === \"5\")) {\r\n return [letter + acc, oct];\r\n }\r\n if (NUM_TYPES.test(oct)) {\r\n return [letter + acc, oct + type];\r\n }\r\n else {\r\n return [letter + acc + oct, type];\r\n }\r\n}\r\n/**\r\n * Get a Chord from a chord name.\r\n */\r\nfunction get(src) {\r\n const { type, tonic } = findChord(src);\r\n if (!type || type.empty) {\r\n return NoChord;\r\n }\r\n const notes = tonic\r\n ? type.intervals.map(i => transpose$1(tonic, i))\r\n : [];\r\n const name = tonic ? tonic + \" \" + type.name : type.name;\r\n return { ...type, name, type: type.name, tonic: tonic || \"\", notes };\r\n}\r\nconst chord = deprecate(\"Chord.chord\", \"Chord.get\", get);\r\nfunction findChord(src) {\r\n if (!src || !src.length) {\r\n return {};\r\n }\r\n const tokens = Array.isArray(src) ? src : tokenize(src);\r\n const tonic = note(tokens[0]).name;\r\n const type = get$1(tokens[1]);\r\n if (!type.empty) {\r\n return { tonic, type };\r\n }\r\n else if (tonic && typeof src === \"string\") {\r\n return { tonic: \"\", type: get$1(src) };\r\n }\r\n else {\r\n return {};\r\n }\r\n}\r\n/**\r\n * Transpose a chord name\r\n *\r\n * @param {string} chordName - the chord name\r\n * @return {string} the transposed chord\r\n *\r\n * @example\r\n * transpose('Dm7', 'P4') // => 'Gm7\r\n */\r\nfunction transpose(chordName, interval) {\r\n const [tonic, type] = tokenize(chordName);\r\n if (!tonic) {\r\n return name;\r\n }\r\n return transpose$1(tonic, interval) + type;\r\n}\r\n/**\r\n * Get all scales where the given chord fits\r\n *\r\n * @example\r\n * chordScales('C7b9')\r\n * // => [\"phrygian dominant\", \"flamenco\", \"spanish heptatonic\", \"half-whole diminished\", \"chromatic\"]\r\n */\r\nfunction chordScales(name) {\r\n const s = get(name);\r\n const isChordIncluded = isSupersetOf(s.chroma);\r\n return all()\r\n .filter(scale => isChordIncluded(scale.chroma))\r\n .map(scale => scale.name);\r\n}\r\n/**\r\n * Get all chords names that are a superset of the given one\r\n * (has the same notes and at least one more)\r\n *\r\n * @function\r\n * @example\r\n * extended(\"CMaj7\")\r\n * // => [ 'Cmaj#4', 'Cmaj7#9#11', 'Cmaj9', 'CM7add13', 'Cmaj13', 'Cmaj9#11', 'CM13#11', 'CM7b9' ]\r\n */\r\nfunction extended(chordName) {\r\n const s = get(chordName);\r\n const isSuperset = isSupersetOf(s.chroma);\r\n return all$1()\r\n .filter(chord => isSuperset(chord.chroma))\r\n .map(chord => s.tonic + chord.aliases[0]);\r\n}\r\n/**\r\n * Find all chords names that are a subset of the given one\r\n * (has less notes but all from the given chord)\r\n *\r\n * @example\r\n */\r\nfunction reduced(chordName) {\r\n const s = get(chordName);\r\n const isSubset = isSubsetOf(s.chroma);\r\n return all$1()\r\n .filter(chord => isSubset(chord.chroma))\r\n .map(chord => s.tonic + chord.aliases[0]);\r\n}\r\nvar index = {\r\n get,\r\n detect,\r\n chordScales,\r\n extended,\r\n reduced,\r\n tokenize,\r\n transpose,\r\n // deprecate\r\n chord\r\n};\n\nexport default index;\nexport { chord, chordScales, extended, get, reduced, tokenize, transpose };\n//# sourceMappingURL=index.esnext.js.map\n","import { interval, distance as distance$1, coordToInterval } from '@tonaljs/core';\n\n/**\r\n * Get the natural list of names\r\n */\r\nfunction names() {\r\n return \"1P 2M 3M 4P 5P 6m 7m\".split(\" \");\r\n}\r\n/**\r\n * Get properties of an interval\r\n *\r\n * @function\r\n * @example\r\n * Interval.get('P4') // => {\"alt\": 0, \"dir\": 1, \"name\": \"4P\", \"num\": 4, \"oct\": 0, \"q\": \"P\", \"semitones\": 5, \"simple\": 4, \"step\": 3, \"type\": \"perfectable\"}\r\n */\r\nconst get = interval;\r\n/**\r\n * Get name of an interval\r\n *\r\n * @function\r\n * @example\r\n * Interval.name('4P') // => \"4P\"\r\n * Interval.name('P4') // => \"4P\"\r\n * Interval.name('C4') // => \"\"\r\n */\r\nconst name = (name) => interval(name).name;\r\n/**\r\n * Get semitones of an interval\r\n * @function\r\n * @example\r\n * Interval.semitones('P4') // => 5\r\n */\r\nconst semitones = (name) => interval(name).semitones;\r\n/**\r\n * Get quality of an interval\r\n * @function\r\n * @example\r\n * Interval.quality('P4') // => \"P\"\r\n */\r\nconst quality = (name) => interval(name).q;\r\n/**\r\n * Get number of an interval\r\n * @function\r\n * @example\r\n * Interval.num('P4') // => 4\r\n */\r\nconst num = (name) => interval(name).num;\r\n/**\r\n * Get the simplified version of an interval.\r\n *\r\n * @function\r\n * @param {string} interval - the interval to simplify\r\n * @return {string} the simplified interval\r\n *\r\n * @example\r\n * Interval.simplify(\"9M\") // => \"2M\"\r\n * Interval.simplify(\"2M\") // => \"2M\"\r\n * Interval.simplify(\"-2M\") // => \"7m\"\r\n * [\"8P\", \"9M\", \"10M\", \"11P\", \"12P\", \"13M\", \"14M\", \"15P\"].map(Interval.simplify)\r\n * // => [ \"8P\", \"2M\", \"3M\", \"4P\", \"5P\", \"6M\", \"7M\", \"8P\" ]\r\n */\r\nfunction simplify(name) {\r\n const i = interval(name);\r\n return i.empty ? \"\" : i.simple + i.q;\r\n}\r\n/**\r\n * Get the inversion (https://en.wikipedia.org/wiki/Inversion_(music)#Intervals)\r\n * of an interval.\r\n *\r\n * @function\r\n * @param {string} interval - the interval to invert in interval shorthand\r\n * notation or interval array notation\r\n * @return {string} the inverted interval\r\n *\r\n * @example\r\n * Interval.invert(\"3m\") // => \"6M\"\r\n * Interval.invert(\"2M\") // => \"7m\"\r\n */\r\nfunction invert(name) {\r\n const i = interval(name);\r\n if (i.empty) {\r\n return \"\";\r\n }\r\n const step = (7 - i.step) % 7;\r\n const alt = i.type === \"perfectable\" ? -i.alt : -(i.alt + 1);\r\n return interval({ step, alt, oct: i.oct, dir: i.dir }).name;\r\n}\r\n// interval numbers\r\nconst IN = [1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7];\r\n// interval qualities\r\nconst IQ = \"P m M m M P d P m M m M\".split(\" \");\r\n/**\r\n * Get interval name from semitones number. Since there are several interval\r\n * names for the same number, the name it's arbitrary, but deterministic.\r\n *\r\n * @param {Integer} num - the number of semitones (can be negative)\r\n * @return {string} the interval name\r\n * @example\r\n * Interval.fromSemitones(7) // => \"5P\"\r\n * Interval.fromSemitones(-7) // => \"-5P\"\r\n */\r\nfunction fromSemitones(semitones) {\r\n const d = semitones < 0 ? -1 : 1;\r\n const n = Math.abs(semitones);\r\n const c = n % 12;\r\n const o = Math.floor(n / 12);\r\n return d * (IN[c] + 7 * o) + IQ[c];\r\n}\r\n/**\r\n * Find interval between two notes\r\n *\r\n * @example\r\n * Interval.distance(\"C4\", \"G4\"); // => \"5P\"\r\n */\r\nconst distance = distance$1;\r\n/**\r\n * Adds two intervals\r\n *\r\n * @function\r\n * @param {string} interval1\r\n * @param {string} interval2\r\n * @return {string} the added interval name\r\n * @example\r\n * Interval.add(\"3m\", \"5P\") // => \"7m\"\r\n */\r\nconst add = combinator((a, b) => [a[0] + b[0], a[1] + b[1]]);\r\n/**\r\n * Returns a function that adds an interval\r\n *\r\n * @function\r\n * @example\r\n * ['1P', '2M', '3M'].map(Interval.addTo('5P')) // => [\"5P\", \"6M\", \"7M\"]\r\n */\r\nconst addTo = (interval) => (other) => add(interval, other);\r\n/**\r\n * Subtracts two intervals\r\n *\r\n * @function\r\n * @param {string} minuendInterval\r\n * @param {string} subtrahendInterval\r\n * @return {string} the substracted interval name\r\n * @example\r\n * Interval.substract('5P', '3M') // => '3m'\r\n * Interval.substract('3M', '5P') // => '-3m'\r\n */\r\nconst substract = combinator((a, b) => [a[0] - b[0], a[1] - b[1]]);\r\nvar index = {\r\n names,\r\n get,\r\n name,\r\n num,\r\n semitones,\r\n quality,\r\n fromSemitones,\r\n distance,\r\n invert,\r\n simplify,\r\n add,\r\n addTo,\r\n substract\r\n};\r\nfunction combinator(fn) {\r\n return (a, b) => {\r\n const coordA = interval(a).coord;\r\n const coordB = interval(b).coord;\r\n if (coordA && coordB) {\r\n const coord = fn(coordA, coordB);\r\n return coordToInterval(coord).name;\r\n }\r\n };\r\n}\n\nexport default index;\nexport { add, addTo, distance, fromSemitones, get, invert, name, names, num, quality, semitones, simplify, substract };\n//# sourceMappingURL=index.esnext.js.map\n","import { note } from '@tonaljs/core';\n\nfunction isMidi(arg) {\r\n return +arg >= 0 && +arg <= 127;\r\n}\r\n/**\r\n * Get the note midi number (a number between 0 and 127)\r\n *\r\n * It returns undefined if not valid note name\r\n *\r\n * @function\r\n * @param {string|number} note - the note name or midi number\r\n * @return {Integer} the midi number or undefined if not valid note\r\n * @example\r\n * import { toMidi } from '@tonaljs/midi'\r\n * toMidi(\"C4\") // => 60\r\n * toMidi(60) // => 60\r\n * toMidi('60') // => 60\r\n */\r\nfunction toMidi(note$1) {\r\n if (isMidi(note$1)) {\r\n return +note$1;\r\n }\r\n const n = note(note$1);\r\n return n.empty ? null : n.midi;\r\n}\r\n/**\r\n * Get the frequency in hertzs from midi number\r\n *\r\n * @param {number} midi - the note midi number\r\n * @param {number} [tuning = 440] - A4 tuning frequency in Hz (440 by default)\r\n * @return {number} the frequency or null if not valid note midi\r\n * @example\r\n * import { midiToFreq} from '@tonaljs/midi'\r\n * midiToFreq(69) // => 440\r\n */\r\nfunction midiToFreq(midi, tuning = 440) {\r\n return Math.pow(2, (midi - 69) / 12) * tuning;\r\n}\r\nconst L2 = Math.log(2);\r\nconst L440 = Math.log(440);\r\n/**\r\n * Get the midi number from a frequency in hertz. The midi number can\r\n * contain decimals (with two digits precission)\r\n *\r\n * @param {number} frequency\r\n * @return {number}\r\n * @example\r\n * import { freqToMidi} from '@tonaljs/midi'\r\n * freqToMidi(220)); //=> 57\r\n * freqToMidi(261.62)); //=> 60\r\n * freqToMidi(261)); //=> 59.96\r\n */\r\nfunction freqToMidi(freq) {\r\n const v = (12 * (Math.log(freq) - L440)) / L2 + 69;\r\n return Math.round(v * 100) / 100;\r\n}\r\nconst SHARPS = \"C C# D D# E F F# G G# A A# B\".split(\" \");\r\nconst FLATS = \"C Db D Eb E F Gb G Ab A Bb B\".split(\" \");\r\n/**\r\n * Given a midi number, returns a note name. The altered notes will have\r\n * flats unless explicitly set with the optional `useSharps` parameter.\r\n *\r\n * @function\r\n * @param {number} midi - the midi note number\r\n * @param {Object} options = default: `{ sharps: false, pitchClass: false }`\r\n * @param {boolean} useSharps - (Optional) set to true to use sharps instead of flats\r\n * @return {string} the note name\r\n * @example\r\n * import { midiToNoteName } from '@tonaljs/midi'\r\n * midiToNoteName(61) // => \"Db4\"\r\n * midiToNoteName(61, { pitchClass: true }) // => \"Db\"\r\n * midiToNoteName(61, { sharps: true }) // => \"C#4\"\r\n * midiToNoteName(61, { pitchClass: true, sharps: true }) // => \"C#\"\r\n * // it rounds to nearest note\r\n * midiToNoteName(61.7) // => \"D4\"\r\n */\r\nfunction midiToNoteName(midi, options = {}) {\r\n midi = Math.round(midi);\r\n const pcs = options.sharps === true ? SHARPS : FLATS;\r\n const pc = pcs[midi % 12];\r\n if (options.pitchClass) {\r\n return pc;\r\n }\r\n const o = Math.floor(midi / 12) - 1;\r\n return pc + o;\r\n}\r\nvar index = { isMidi, toMidi, midiToFreq, midiToNoteName, freqToMidi };\n\nexport default index;\nexport { freqToMidi, isMidi, midiToFreq, midiToNoteName, toMidi };\n//# sourceMappingURL=index.esnext.js.map\n","import { note, transpose as transpose$1, coordToNote } from '@tonaljs/core';\nimport { midiToNoteName } from '@tonaljs/midi';\n\nconst NAMES = [\"C\", \"D\", \"E\", \"F\", \"G\", \"A\", \"B\"];\r\nconst toName = (n) => n.name;\r\nconst onlyNotes = (array) => array.map(note).filter(n => !n.empty);\r\n/**\r\n * Return the natural note names without octave\r\n * @function\r\n * @example\r\n * Note.names(); // => [\"C\", \"D\", \"E\", \"F\", \"G\", \"A\", \"B\"]\r\n */\r\nfunction names(array) {\r\n if (array === undefined) {\r\n return NAMES.slice();\r\n }\r\n else if (!Array.isArray(array)) {\r\n return [];\r\n }\r\n else {\r\n return onlyNotes(array).map(toName);\r\n }\r\n}\r\n/**\r\n * Get a note from a note name\r\n *\r\n * @function\r\n * @example\r\n * Note.get('Bb4') // => { name: \"Bb4\", midi: 70, chroma: 10, ... }\r\n */\r\nconst get = note;\r\n/**\r\n * Get the note name\r\n * @function\r\n */\r\nconst name = (note) => get(note).name;\r\n/**\r\n * Get the note pitch class name\r\n * @function\r\n */\r\nconst pitchClass = (note) => get(note).pc;\r\n/**\r\n * Get the note accidentals\r\n * @function\r\n */\r\nconst accidentals = (note) => get(note).acc;\r\n/**\r\n * Get the note octave\r\n * @function\r\n */\r\nconst octave = (note) => get(note).oct;\r\n/**\r\n * Get the note midi\r\n * @function\r\n */\r\nconst midi = (note) => get(note).midi;\r\n/**\r\n * Get the note midi\r\n * @function\r\n */\r\nconst freq = (note) => get(note).freq;\r\n/**\r\n * Get the note chroma\r\n * @function\r\n */\r\nconst chroma = (note) => get(note).chroma;\r\n/**\r\n * Given a midi number, returns a note name. Uses flats for altered notes.\r\n *\r\n * @function\r\n * @param {number} midi - the midi note number\r\n * @return {string} the note name\r\n * @example\r\n * Note.fromMidi(61) // => \"Db4\"\r\n * Note.fromMidi(61.7) // => \"D4\"\r\n */\r\nfunction fromMidi(midi) {\r\n return midiToNoteName(midi);\r\n}\r\n/**\r\n * Given a midi number, returns a note name. Uses flats for altered notes.\r\n *\r\n * @function\r\n * @param {number} midi - the midi note number\r\n * @return {string} the note name\r\n * @example\r\n * Note.fromMidiSharps(61) // => \"C#4\"\r\n */\r\nfunction fromMidiSharps(midi) {\r\n return midiToNoteName(midi, { sharps: true });\r\n}\r\n/**\r\n * Transpose a note by an interval\r\n */\r\nconst transpose = transpose$1;\r\nconst tr = transpose$1;\r\n/**\r\n * Transpose by an interval.\r\n * @function\r\n * @param {string} interval\r\n * @return {function} a function that transposes by the given interval\r\n * @example\r\n * [\"C\", \"D\", \"E\"].map(Note.transposeBy(\"5P\"));\r\n * // => [\"G\", \"A\", \"B\"]\r\n */\r\nconst transposeBy = (interval) => (note) => transpose(note, interval);\r\nconst trBy = transposeBy;\r\n/**\r\n * Transpose from a note\r\n * @function\r\n * @param {string} note\r\n * @return {function} a function that transposes the the note by an interval\r\n * [\"1P\", \"3M\", \"5P\"].map(Note.transposeFrom(\"C\"));\r\n * // => [\"C\", \"E\", \"G\"]\r\n */\r\nconst transposeFrom = (note) => (interval) => transpose(note, interval);\r\nconst trFrom = transposeFrom;\r\n/**\r\n * Transpose a note by a number of perfect fifths.\r\n *\r\n * @function\r\n * @param {string} note - the note name\r\n * @param {number} fifhts - the number of fifths\r\n * @return {string} the transposed note name\r\n *\r\n * @example\r\n * import { transposeFifths } from \"@tonaljs/note\"\r\n * transposeFifths(\"G4\", 1) // => \"D\"\r\n * [0, 1, 2, 3, 4].map(fifths => transposeFifths(\"C\", fifths)) // => [\"C\", \"G\", \"D\", \"A\", \"E\"]\r\n */\r\nfunction transposeFifths(noteName, fifths) {\r\n const note = get(noteName);\r\n if (note.empty) {\r\n return \"\";\r\n }\r\n const [nFifths, nOcts] = note.coord;\r\n const transposed = nOcts === undefined\r\n ? coordToNote([nFifths + fifths])\r\n : coordToNote([nFifths + fifths, nOcts]);\r\n return transposed.name;\r\n}\r\nconst trFifths = transposeFifths;\r\nconst ascending = (a, b) => a.height - b.height;\r\nconst descending = (a, b) => b.height - a.height;\r\nfunction sortedNames(notes, comparator) {\r\n comparator = comparator || ascending;\r\n return onlyNotes(notes)\r\n .sort(comparator)\r\n .map(toName);\r\n}\r\nfunction sortedUniqNames(notes) {\r\n return sortedNames(notes, ascending).filter((n, i, a) => i === 0 || n !== a[i - 1]);\r\n}\r\n/**\r\n * Simplify a note\r\n *\r\n * @function\r\n * @param {string} note - the note to be simplified\r\n * - sameAccType: default true. Use same kind of accidentals that source\r\n * @return {string} the simplified note or '' if not valid note\r\n * @example\r\n * simplify(\"C##\") // => \"D\"\r\n * simplify(\"C###\") // => \"D#\"\r\n * simplify(\"C###\")\r\n * simplify(\"B#4\") // => \"C5\"\r\n */\r\nconst simplify = nameBuilder(true);\r\n/**\r\n * Get enharmonic of a note\r\n *\r\n * @function\r\n * @param {string} note\r\n * @return {string} the enharmonic note or '' if not valid note\r\n * @example\r\n * Note.enharmonic(\"Db\") // => \"C#\"\r\n * Note.enharmonic(\"C\") // => \"C\"\r\n */\r\nconst enharmonic = nameBuilder(false);\r\nfunction nameBuilder(sameAccidentals) {\r\n return (noteName) => {\r\n const note = get(noteName);\r\n if (note.empty) {\r\n return \"\";\r\n }\r\n const sharps = sameAccidentals ? note.alt > 0 : note.alt < 0;\r\n const pitchClass = note.midi === null;\r\n return midiToNoteName(note.midi || note.chroma, { sharps, pitchClass });\r\n };\r\n}\r\nvar index = {\r\n names,\r\n get,\r\n name,\r\n pitchClass,\r\n accidentals,\r\n octave,\r\n midi,\r\n ascending,\r\n descending,\r\n sortedNames,\r\n sortedUniqNames,\r\n fromMidi,\r\n fromMidiSharps,\r\n freq,\r\n chroma,\r\n transpose,\r\n tr,\r\n transposeBy,\r\n trBy,\r\n transposeFrom,\r\n trFrom,\r\n transposeFifths,\r\n trFifths,\r\n simplify,\r\n enharmonic\r\n};\n\nexport default index;\nexport { accidentals, ascending, chroma, descending, enharmonic, freq, fromMidi, fromMidiSharps, get, midi, name, names, octave, pitchClass, simplify, sortedNames, sortedUniqNames, tr, trBy, trFifths, trFrom, transpose, transposeBy, transposeFifths, transposeFrom };\n//# sourceMappingURL=index.esnext.js.map\n","import { isPitch, altToAcc, isNamed, deprecate, accToAlt, interval } from '@tonaljs/core';\n\nconst NoRomanNumeral = { empty: true, name: \"\", chordType: \"\" };\r\nconst cache = {};\r\n/**\r\n * Get properties of a roman numeral string\r\n *\r\n * @function\r\n * @param {string} - the roman numeral string (can have type, like: Imaj7)\r\n * @return {Object} - the roman numeral properties\r\n * @param {string} name - the roman numeral (tonic)\r\n * @param {string} type - the chord type\r\n * @param {string} num - the number (1 = I, 2 = II...)\r\n * @param {boolean} major - major or not\r\n *\r\n * @example\r\n * romanNumeral(\"VIIb5\") // => { name: \"VII\", type: \"b5\", num: 7, major: true }\r\n */\r\nfunction get(src) {\r\n return typeof src === \"string\"\r\n ? cache[src] || (cache[src] = parse(src))\r\n : typeof src === \"number\"\r\n ? get(NAMES[src] || \"\")\r\n : isPitch(src)\r\n ? fromPitch(src)\r\n : isNamed(src)\r\n ? get(src.name)\r\n : NoRomanNumeral;\r\n}\r\nconst romanNumeral = deprecate(\"RomanNumeral.romanNumeral\", \"RomanNumeral.get\", get);\r\n/**\r\n * Get roman numeral names\r\n *\r\n * @function\r\n * @param {boolean} [isMajor=true]\r\n * @return {Array}\r\n *\r\n * @example\r\n * names() // => [\"I\", \"II\", \"III\", \"IV\", \"V\", \"VI\", \"VII\"]\r\n */\r\nfunction names(major = true) {\r\n return (major ? NAMES : NAMES_MINOR).slice();\r\n}\r\nfunction fromPitch(pitch) {\r\n return get(altToAcc(pitch.alt) + NAMES[pitch.step]);\r\n}\r\nconst REGEX = /^(#{1,}|b{1,}|x{1,}|)(IV|I{1,3}|VI{0,2}|iv|i{1,3}|vi{0,2})([^IViv]*)$/;\r\nfunction tokenize(str) {\r\n return (REGEX.exec(str) || [\"\", \"\", \"\", \"\"]);\r\n}\r\nconst ROMANS = \"I II III IV V VI VII\";\r\nconst NAMES = ROMANS.split(\" \");\r\nconst NAMES_MINOR = ROMANS.toLowerCase().split(\" \");\r\nfunction parse(src) {\r\n const [name, acc, roman, chordType] = tokenize(src);\r\n if (!roman) {\r\n return NoRomanNumeral;\r\n }\r\n const upperRoman = roman.toUpperCase();\r\n const step = NAMES.indexOf(upperRoman);\r\n const alt = accToAlt(acc);\r\n const dir = 1;\r\n return {\r\n empty: false,\r\n name,\r\n roman,\r\n interval: interval({ step, alt, dir }).name,\r\n acc,\r\n chordType,\r\n alt,\r\n step,\r\n major: roman === upperRoman,\r\n oct: 0,\r\n dir\r\n };\r\n}\r\nvar index = {\r\n names,\r\n get,\r\n // deprecated\r\n romanNumeral\r\n};\n\nexport default index;\nexport { get, names, tokenize };\n//# sourceMappingURL=index.esnext.js.map\n","import { transpose, altToAcc, accToAlt, note } from '@tonaljs/core';\nimport { transposeFifths } from '@tonaljs/note';\nimport { get } from '@tonaljs/roman-numeral';\n\nconst mapToScale = (scale) => (symbols, sep = \"\") => symbols.map((symbol, index) => symbol !== \"-\" ? scale[index] + sep + symbol : \"\");\r\nfunction keyScale(gradesLiteral, chordsLiteral, hfLiteral, chordScalesLiteral) {\r\n return (tonic) => {\r\n const grades = gradesLiteral.split(\" \");\r\n const intervals = grades.map(gr => get(gr).interval || \"\");\r\n const scale = intervals.map(interval => transpose(tonic, interval));\r\n const map = mapToScale(scale);\r\n return {\r\n tonic,\r\n grades,\r\n intervals,\r\n scale,\r\n chords: map(chordsLiteral.split(\" \")),\r\n chordsHarmonicFunction: hfLiteral.split(\" \"),\r\n chordScales: map(chordScalesLiteral.split(\",\"), \" \")\r\n };\r\n };\r\n}\r\nconst distInFifths = (from, to) => {\r\n const f = note(from);\r\n const t = note(to);\r\n return f.empty || t.empty ? 0 : t.coord[0] - f.coord[0];\r\n};\r\nconst MajorScale = keyScale(\"I II III IV V VI VII\", \"maj7 m7 m7 maj7 7 m7 m7b5\", \"T SD T SD D T D\", \"major,dorian,phrygian,lydian,mixolydian,minor,locrian\");\r\nconst NaturalScale = keyScale(\"I II bIII IV V bVI bVII\", \"m7 m7b5 maj7 m7 m7 maj7 7\", \"T SD T SD D SD SD\", \"minor,locrian,major,dorian,phrygian,lydian,mixolydian\");\r\nconst HarmonicScale = keyScale(\"I II bIII IV V bVI VII\", \"mmaj7 m7b5 +maj7 m7 7 maj7 mo7\", \"T SD T SD D SD D\", \"harmonic minor,locrian 6,major augmented,lydian diminished,phrygian dominant,lydian #9,ultralocrian\");\r\nconst MelodicScale = keyScale(\"I II bIII IV V VI VII\", \"m6 m7 +maj7 7 7 m7b5 m7b5\", \"T SD T SD D - -\", \"melodic minor,dorian b2,lydian augmented,lydian dominant,mixolydian b6,locrian #2,altered\");\r\n/**\r\n * Get a major key properties in a given tonic\r\n * @param tonic\r\n */\r\nfunction majorKey(tonic) {\r\n const keyScale = MajorScale(tonic);\r\n const alteration = distInFifths(\"C\", tonic);\r\n const map = mapToScale(keyScale.scale);\r\n return {\r\n ...keyScale,\r\n type: \"major\",\r\n minorRelative: transpose(tonic, \"-3m\"),\r\n alteration,\r\n keySignature: altToAcc(alteration),\r\n secondaryDominants: map(\"- VI7 VII7 I7 II7 III7 -\".split(\" \")),\r\n secondaryDominantsMinorRelative: map(\"- IIIm7b5 IV#m7 Vm7 VIm7 VIIm7b5 -\".split(\" \")),\r\n substituteDominants: map(\"- bIII7 IV7 bV7 bVI7 bVII7 -\".split(\" \")),\r\n substituteDominantsMinorRelative: map(\"- IIIm7 Im7 IIbm7 VIm7 IVm7 -\".split(\" \"))\r\n };\r\n}\r\n/**\r\n * Get minor key properties in a given tonic\r\n * @param tonic\r\n */\r\nfunction minorKey(tonic) {\r\n const alteration = distInFifths(\"C\", tonic) - 3;\r\n return {\r\n type: \"minor\",\r\n tonic,\r\n relativeMajor: transpose(tonic, \"3m\"),\r\n alteration,\r\n keySignature: altToAcc(alteration),\r\n natural: NaturalScale(tonic),\r\n harmonic: HarmonicScale(tonic),\r\n melodic: MelodicScale(tonic)\r\n };\r\n}\r\n/**\r\n * Given a key signature, returns the tonic of the major key\r\n * @param sigature\r\n * @example\r\n * majorTonicFromKeySignature('###') // => 'A'\r\n */\r\nfunction majorTonicFromKeySignature(sig) {\r\n if (typeof sig === \"number\") {\r\n return transposeFifths(\"C\", sig);\r\n }\r\n else if (typeof sig === \"string\" && /^b+|#+$/.test(sig)) {\r\n return transposeFifths(\"C\", accToAlt(sig));\r\n }\r\n return null;\r\n}\r\nvar index = { majorKey, majorTonicFromKeySignature, minorKey };\n\nexport default index;\nexport { majorKey, majorTonicFromKeySignature, minorKey };\n//# sourceMappingURL=index.esnext.js.map\n","import { deprecate } from '@tonaljs/core';\nimport { chromaToIntervals, EmptyPcset } from '@tonaljs/pcset';\n\nconst DATA = [\r\n [0, 2773, 0, \"ionian\", \"\", \"Maj7\", \"major\"],\r\n [1, 2902, 2, \"dorian\", \"m\", \"m7\"],\r\n [2, 3418, 4, \"phrygian\", \"m\", \"m7\"],\r\n [3, 2741, -1, \"lydian\", \"\", \"Maj7\"],\r\n [4, 2774, 1, \"mixolydian\", \"\", \"7\"],\r\n [5, 2906, 3, \"aeolian\", \"m\", \"m7\", \"minor\"],\r\n [6, 3434, 5, \"locrian\", \"dim\", \"m7b5\"]\r\n];\n\nconst NoMode = {\r\n ...EmptyPcset,\r\n name: \"\",\r\n alt: 0,\r\n modeNum: NaN,\r\n triad: \"\",\r\n seventh: \"\",\r\n aliases: []\r\n};\r\nconst modes = DATA.map(toMode);\r\nconst index = {};\r\nmodes.forEach(mode => {\r\n index[mode.name] = mode;\r\n mode.aliases.forEach(alias => {\r\n index[alias] = mode;\r\n });\r\n});\r\n/**\r\n * Get a Mode by it's name\r\n *\r\n * @example\r\n * get('dorian')\r\n * // =>\r\n * // {\r\n * // intervals: [ '1P', '2M', '3m', '4P', '5P', '6M', '7m' ],\r\n * // modeNum: 1,\r\n * // chroma: '101101010110',\r\n * // normalized: '101101010110',\r\n * // name: 'dorian',\r\n * // setNum: 2902,\r\n * // alt: 2,\r\n * // triad: 'm',\r\n * // seventh: 'm7',\r\n * // aliases: []\r\n * // }\r\n */\r\nfunction get(name) {\r\n return typeof name === \"string\"\r\n ? index[name.toLowerCase()] || NoMode\r\n : name && name.name\r\n ? get(name.name)\r\n : NoMode;\r\n}\r\nconst mode = deprecate(\"Mode.mode\", \"Mode.get\", get);\r\n/**\r\n * Get a list of all modes\r\n */\r\nfunction all() {\r\n return modes.slice();\r\n}\r\nconst entries = deprecate(\"Mode.mode\", \"Mode.all\", all);\r\n/**\r\n * Get a list of all mode names\r\n */\r\nfunction names() {\r\n return modes.map(mode => mode.name);\r\n}\r\nfunction toMode(mode) {\r\n const [modeNum, setNum, alt, name, triad, seventh, alias] = mode;\r\n const aliases = alias ? [alias] : [];\r\n const chroma = Number(setNum).toString(2);\r\n const intervals = chromaToIntervals(chroma);\r\n return {\r\n empty: false,\r\n intervals,\r\n modeNum,\r\n chroma,\r\n normalized: chroma,\r\n name,\r\n setNum,\r\n alt,\r\n triad,\r\n seventh,\r\n aliases\r\n };\r\n}\r\nvar index$1 = {\r\n get,\r\n names,\r\n all,\r\n // deprecated\r\n entries,\r\n mode\r\n};\n\nexport default index$1;\nexport { all, entries, get, mode, names };\n//# sourceMappingURL=index.esnext.js.map\n","import { tokenize } from '@tonaljs/chord';\nimport { transpose, interval, distance } from '@tonaljs/core';\nimport { get } from '@tonaljs/roman-numeral';\n\n/**\r\n * Given a tonic and a chord list expressed with roman numeral notation\r\n * returns the progression expressed with leadsheet chords symbols notation\r\n * @example\r\n * fromRomanNumerals(\"C\", [\"I\", \"IIm7\", \"V7\"]);\r\n * // => [\"C\", \"Dm7\", \"G7\"]\r\n */\r\nfunction fromRomanNumerals(tonic, chords) {\r\n const romanNumerals = chords.map(get);\r\n return romanNumerals.map(rn => transpose(tonic, interval(rn)) + rn.chordType);\r\n}\r\n/**\r\n * Given a tonic and a chord list with leadsheet symbols notation,\r\n * return the chord list with roman numeral notation\r\n * @example\r\n * toRomanNumerals(\"C\", [\"CMaj7\", \"Dm7\", \"G7\"]);\r\n * // => [\"IMaj7\", \"IIm7\", \"V7\"]\r\n */\r\nfunction toRomanNumerals(tonic, chords) {\r\n return chords.map(chord => {\r\n const [note, chordType] = tokenize(chord);\r\n const intervalName = distance(tonic, note);\r\n const roman = get(interval(intervalName));\r\n return roman.name + chordType;\r\n });\r\n}\r\nvar index = { fromRomanNumerals, toRomanNumerals };\n\nexport default index;\nexport { fromRomanNumerals, toRomanNumerals };\n//# sourceMappingURL=index.esnext.js.map\n","import { compact, range } from '@tonaljs/collection';\nimport { toMidi, midiToNoteName } from '@tonaljs/midi';\n\n/**\r\n * Create a numeric range. You supply a list of notes or numbers and it will\r\n * be connected to create complex ranges.\r\n *\r\n * @param {Array} array - the list of notes or numbers used\r\n * @return {Array} an array of numbers or empty array if not valid parameters\r\n *\r\n * @example\r\n * numeric([\"C5\", \"C4\"]) // => [ 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60 ]\r\n * // it works midi notes\r\n * numeric([10, 5]) // => [ 10, 9, 8, 7, 6, 5 ]\r\n * // complex range\r\n * numeric([\"C4\", \"E4\", \"Bb3\"]) // => [60, 61, 62, 63, 64, 63, 62, 61, 60, 59, 58]\r\n */\r\nfunction numeric(notes) {\r\n const midi = compact(notes.map(toMidi));\r\n if (!notes.length || midi.length !== notes.length) {\r\n // there is no valid notes\r\n return [];\r\n }\r\n return midi.reduce((result, note) => {\r\n const last = result[result.length - 1];\r\n return result.concat(range(last, note).slice(1));\r\n }, [midi[0]]);\r\n}\r\n/**\r\n * Create a range of chromatic notes. The altered notes will use flats.\r\n *\r\n * @function\r\n * @param {String|Array} list - the list of notes or midi note numbers\r\n * @return {Array} an array of note names\r\n *\r\n * @example\r\n * Range.chromatic(\"C2 E2 D2\") // => [\"C2\", \"Db2\", \"D2\", \"Eb2\", \"E2\", \"Eb2\", \"D2\"]\r\n * // with sharps\r\n * Range.chromatic(\"C2 C3\", true) // => [ \"C2\", \"C#2\", \"D2\", \"D#2\", \"E2\", \"F2\", \"F#2\", \"G2\", \"G#2\", \"A2\", \"A#2\", \"B2\", \"C3\" ]\r\n */\r\nfunction chromatic(notes, options) {\r\n return numeric(notes).map(midi => midiToNoteName(midi, options));\r\n}\r\nvar index = { numeric, chromatic };\n\nexport default index;\nexport { chromatic, numeric };\n//# sourceMappingURL=index.esnext.js.map\n","import { all } from '@tonaljs/chord-type';\nimport { rotate } from '@tonaljs/collection';\nimport { note, transpose, deprecate } from '@tonaljs/core';\nimport { sortedUniqNames } from '@tonaljs/note';\nimport { isSubsetOf, isSupersetOf, modes } from '@tonaljs/pcset';\nimport { names as names$1, get as get$1, all as all$1 } from '@tonaljs/scale-type';\n\n/**\r\n * References:\r\n * - https://www.researchgate.net/publication/327567188_An_Algorithm_for_Spelling_the_Pitches_of_Any_Musical_Scale\r\n * @module scale\r\n */\r\nconst NoScale = {\r\n empty: true,\r\n name: \"\",\r\n type: \"\",\r\n tonic: null,\r\n setNum: NaN,\r\n chroma: \"\",\r\n normalized: \"\",\r\n aliases: [],\r\n notes: [],\r\n intervals: []\r\n};\r\n/**\r\n * Given a string with a scale name and (optionally) a tonic, split\r\n * that components.\r\n *\r\n * It retuns an array with the form [ name, tonic ] where tonic can be a\r\n * note name or null and name can be any arbitrary string\r\n * (this function doesn\"t check if that scale name exists)\r\n *\r\n * @function\r\n * @param {string} name - the scale name\r\n * @return {Array} an array [tonic, name]\r\n * @example\r\n * tokenize(\"C mixolydean\") // => [\"C\", \"mixolydean\"]\r\n * tokenize(\"anything is valid\") // => [\"\", \"anything is valid\"]\r\n * tokenize() // => [\"\", \"\"]\r\n */\r\nfunction tokenize(name) {\r\n if (typeof name !== \"string\") {\r\n return [\"\", \"\"];\r\n }\r\n const i = name.indexOf(\" \");\r\n const tonic = note(name.substring(0, i));\r\n if (tonic.empty) {\r\n const n = note(name);\r\n return n.empty ? [\"\", name] : [n.name, \"\"];\r\n }\r\n const type = name.substring(tonic.name.length + 1);\r\n return [tonic.name, type.length ? type : \"\"];\r\n}\r\n/**\r\n * Get all scale names\r\n * @function\r\n */\r\nconst names = names$1;\r\n/**\r\n * Get a Scale from a scale name.\r\n */\r\nfunction get(src) {\r\n const tokens = Array.isArray(src) ? src : tokenize(src);\r\n const tonic = note(tokens[0]).name;\r\n const st = get$1(tokens[1]);\r\n if (st.empty) {\r\n return NoScale;\r\n }\r\n const type = st.name;\r\n const notes = tonic\r\n ? st.intervals.map(i => transpose(tonic, i))\r\n : [];\r\n const name = tonic ? tonic + \" \" + type : type;\r\n return { ...st, name, type, tonic, notes };\r\n}\r\nconst scale = deprecate(\"Scale.scale\", \"Scale.get\", get);\r\n/**\r\n * Get all chords that fits a given scale\r\n *\r\n * @function\r\n * @param {string} name - the scale name\r\n * @return {Array} - the chord names\r\n *\r\n * @example\r\n * scaleChords(\"pentatonic\") // => [\"5\", \"64\", \"M\", \"M6\", \"Madd9\", \"Msus2\"]\r\n */\r\nfunction scaleChords(name) {\r\n const s = get(name);\r\n const inScale = isSubsetOf(s.chroma);\r\n return all()\r\n .filter(chord => inScale(chord.chroma))\r\n .map(chord => chord.aliases[0]);\r\n}\r\n/**\r\n * Get all scales names that are a superset of the given one\r\n * (has the same notes and at least one more)\r\n *\r\n * @function\r\n * @param {string} name\r\n * @return {Array} a list of scale names\r\n * @example\r\n * extended(\"major\") // => [\"bebop\", \"bebop dominant\", \"bebop major\", \"chromatic\", \"ichikosucho\"]\r\n */\r\nfunction extended(name) {\r\n const s = get(name);\r\n const isSuperset = isSupersetOf(s.chroma);\r\n return all$1()\r\n .filter(scale => isSuperset(scale.chroma))\r\n .map(scale => scale.name);\r\n}\r\n/**\r\n * Find all scales names that are a subset of the given one\r\n * (has less notes but all from the given scale)\r\n *\r\n * @function\r\n * @param {string} name\r\n * @return {Array} a list of scale names\r\n *\r\n * @example\r\n * reduced(\"major\") // => [\"ionian pentatonic\", \"major pentatonic\", \"ritusen\"]\r\n */\r\nfunction reduced(name) {\r\n const isSubset = isSubsetOf(get(name).chroma);\r\n return all$1()\r\n .filter(scale => isSubset(scale.chroma))\r\n .map(scale => scale.name);\r\n}\r\n/**\r\n * Given an array of notes, return the scale: a pitch class set starting from\r\n * the first note of the array\r\n *\r\n * @function\r\n * @param {string[]} notes\r\n * @return {string[]} pitch classes with same tonic\r\n * @example\r\n * scaleNotes(['C4', 'c3', 'C5', 'C4', 'c4']) // => [\"C\"]\r\n * scaleNotes(['D4', 'c#5', 'A5', 'F#6']) // => [\"D\", \"F#\", \"A\", \"C#\"]\r\n */\r\nfunction scaleNotes(notes) {\r\n const pcset = notes.map(n => note(n).pc).filter(x => x);\r\n const tonic = pcset[0];\r\n const scale = sortedUniqNames(pcset);\r\n return rotate(scale.indexOf(tonic), scale);\r\n}\r\n/**\r\n * Find mode names of a scale\r\n *\r\n * @function\r\n * @param {string} name - scale name\r\n * @example\r\n * modeNames(\"C pentatonic\") // => [\r\n * [\"C\", \"major pentatonic\"],\r\n * [\"D\", \"egyptian\"],\r\n * [\"E\", \"malkos raga\"],\r\n * [\"G\", \"ritusen\"],\r\n * [\"A\", \"minor pentatonic\"]\r\n * ]\r\n */\r\nfunction modeNames(name) {\r\n const s = get(name);\r\n if (s.empty) {\r\n return [];\r\n }\r\n const tonics = s.tonic ? s.notes : s.intervals;\r\n return modes(s.chroma)\r\n .map((chroma, i) => {\r\n const modeName = get(chroma).name;\r\n return modeName ? [tonics[i], modeName] : [\"\", \"\"];\r\n })\r\n .filter(x => x[0]);\r\n}\r\nvar index = {\r\n get,\r\n names,\r\n extended,\r\n modeNames,\r\n reduced,\r\n scaleChords,\r\n scaleNotes,\r\n tokenize,\r\n // deprecated\r\n scale\r\n};\n\nexport default index;\nexport { extended, get, modeNames, names, reduced, scale, scaleChords, scaleNotes, tokenize };\n//# sourceMappingURL=index.esnext.js.map\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tonaljs/abc-notation'), require('@tonaljs/array'), require('@tonaljs/chord'), require('@tonaljs/chord-type'), require('@tonaljs/collection'), require('@tonaljs/core'), require('@tonaljs/interval'), require('@tonaljs/key'), require('@tonaljs/midi'), require('@tonaljs/mode'), require('@tonaljs/note'), require('@tonaljs/pcset'), require('@tonaljs/progression'), require('@tonaljs/range'), require('@tonaljs/roman-numeral'), require('@tonaljs/scale'), require('@tonaljs/scale-type')) :\n typeof define === 'function' && define.amd ? define(['exports', '@tonaljs/abc-notation', '@tonaljs/array', '@tonaljs/chord', '@tonaljs/chord-type', '@tonaljs/collection', '@tonaljs/core', '@tonaljs/interval', '@tonaljs/key', '@tonaljs/midi', '@tonaljs/mode', '@tonaljs/note', '@tonaljs/pcset', '@tonaljs/progression', '@tonaljs/range', '@tonaljs/roman-numeral', '@tonaljs/scale', '@tonaljs/scale-type'], factory) :\n (global = global || self, factory(global.Tonal = {}, global.abcNotation, global.array, global.chord, global.ChordType, global.collection, global.Core, global.interval, global.key, global.midi, global.mode, global.note, global.Pcset, global.progression, global.range, global.romanNumeral, global.scale, global.ScaleType));\n}(this, (function (exports, abcNotation, array, chord, ChordType, collection, Core, interval, key, midi, mode, note, Pcset, progression, range, romanNumeral, scale, ScaleType) { 'use strict';\n\n abcNotation = abcNotation && abcNotation.hasOwnProperty('default') ? abcNotation['default'] : abcNotation;\n chord = chord && chord.hasOwnProperty('default') ? chord['default'] : chord;\n ChordType = ChordType && ChordType.hasOwnProperty('default') ? ChordType['default'] : ChordType;\n collection = collection && collection.hasOwnProperty('default') ? collection['default'] : collection;\n interval = interval && interval.hasOwnProperty('default') ? interval['default'] : interval;\n key = key && key.hasOwnProperty('default') ? key['default'] : key;\n midi = midi && midi.hasOwnProperty('default') ? midi['default'] : midi;\n mode = mode && mode.hasOwnProperty('default') ? mode['default'] : mode;\n note = note && note.hasOwnProperty('default') ? note['default'] : note;\n Pcset = Pcset && Pcset.hasOwnProperty('default') ? Pcset['default'] : Pcset;\n progression = progression && progression.hasOwnProperty('default') ? progression['default'] : progression;\n range = range && range.hasOwnProperty('default') ? range['default'] : range;\n romanNumeral = romanNumeral && romanNumeral.hasOwnProperty('default') ? romanNumeral['default'] : romanNumeral;\n scale = scale && scale.hasOwnProperty('default') ? scale['default'] : scale;\n ScaleType = ScaleType && ScaleType.hasOwnProperty('default') ? ScaleType['default'] : ScaleType;\n\n // deprecated (backwards compatibility)\r\n var Tonal = Core;\r\n var PcSet = Pcset;\r\n var ChordDictionary = ChordType;\r\n var ScaleDictionary = ScaleType;\n\n Object.keys(Core).forEach(function (k) {\n if (k !== 'default') Object.defineProperty(exports, k, {\n enumerable: true,\n get: function () {\n return Core[k];\n }\n });\n });\n exports.AbcNotation = abcNotation;\n exports.Array = array;\n exports.Chord = chord;\n exports.ChordType = ChordType;\n exports.Collection = collection;\n exports.Core = Core;\n exports.Interval = interval;\n exports.Key = key;\n exports.Midi = midi;\n exports.Mode = mode;\n exports.Note = note;\n exports.Pcset = Pcset;\n exports.Progression = progression;\n exports.Range = range;\n exports.RomanNumeral = romanNumeral;\n exports.Scale = scale;\n exports.ScaleType = ScaleType;\n exports.ChordDictionary = ChordDictionary;\n exports.PcSet = PcSet;\n exports.ScaleDictionary = ScaleDictionary;\n exports.Tonal = Tonal;\n\n Object.defineProperty(exports, '__esModule', { value: true });\n\n})));\n//# sourceMappingURL=index.es5.js.map\n"],"names":["fillStr","s","n","Array","Math","abs","join","deprecate","original","alternative","fn","args","console","warn","apply","this","isNamed","src","name","isPitch","pitch","step","alt","FIFTHS","STEPS_TO_OCTS","map","fifths","floor","encode","oct","dir","f","undefined","FIFTHS_TO_STEPS","decode","coord","o","i","unaltered","NoNote","empty","pc","acc","cache","Map","stepToLetter","charAt","altToAcc","accToAlt","length","note","cached","get","value","noteName","tokens","tokenizeNote","letter","octStr","charCodeAt","chroma","SEMI","height","midi","freq","pow","parse","props","pitchName","set","REGEX","str","m","exec","toUpperCase","replace","coordToNote","noteCoord","NoInterval","REGEX$1","RegExp","tokenizeInterval","cache$1","interval","num","q","t","type","simple","test","qToAlt","semitones","SIZES","parse$1","altToQ","pitchName$1","coordToInterval","transpose","intervalName","note$1","interval$1","intervalCoord","distance","fromNote","toNote","from","to","fcoord","tcoord","character","times","tokenize","abcToScientificNotation","a","scientificToAbcNotation","toLowerCase","index","transpose$1","distance$1","sortedNoteNames","notes","filter","sort","b","arr","permutations","slice","reduce","perm","concat","e","pos","newPerm","splice","ascR","descR","len","rnd","random","range","rotate","compact","EmptyPcset","setNum","normalized","intervals","setNumToChroma","Number","toString","chromaToNumber","parseInt","isChroma","[object Object]","isArray","binary","listToChroma","isPcset","normalizedNum","split","_","chromaRotations","chromaToIntervals","chromaToPcset","pcset","IVLS","push","modes","normalize","r","isSubsetOf","isSupersetOf","isNoteIncludedIn","chromas","isEqual","s1","s2","isIncluded","NoChordType","quality","aliases","dictionary","chordType","all","entries","add","fullName","has","indexOf","getQuality","chord","get$1","forEach","alias","addAlias","ivls","names","index$1","x","symbols","removeAll","keys","Object","NotFound","weight","NoScaleType","scale","scaleType","NoChord","tonic","NaN","NUM_TYPES","findChord","detect","source","tonicChroma","pcToName","record","namedSet","mode","chordName","baseNote","findExactMatches","chordScales","isChordIncluded","extended","isSuperset","all$1","reduced","isSubset","IN","IQ","combinator","substract","fromSemitones","d","c","invert","simplify","addTo","other","coordA","coordB","isMidi","arg","toMidi","L2","log","L440","SHARPS","FLATS","midiToNoteName","options","round","sharps","pitchClass","midiToFreq","tuning","freqToMidi","v","NAMES","toName","onlyNotes","array","tr","transposeBy","trBy","transposeFrom","trFrom","transposeFifths","nFifths","nOcts","trFifths","ascending","sortedNames","comparator","sortedUniqNames","nameBuilder","enharmonic","sameAccidentals","accidentals","octave","descending","fromMidi","fromMidiSharps","NoRomanNumeral","roman","upperRoman","major","romanNumeral","ROMANS","NAMES_MINOR","mapToScale","sep","symbol","keyScale","gradesLiteral","chordsLiteral","hfLiteral","chordScalesLiteral","grades","gr","chords","chordsHarmonicFunction","distInFifths","MajorScale","NaturalScale","HarmonicScale","MelodicScale","majorKey","alteration","minorRelative","keySignature","secondaryDominants","secondaryDominantsMinorRelative","substituteDominants","substituteDominantsMinorRelative","majorTonicFromKeySignature","sig","minorKey","relativeMajor","natural","harmonic","melodic","NoMode","modeNum","triad","seventh","fromRomanNumerals","rn","toRomanNumerals","numeric","result","last","chromatic","NoScale","substring","st","names$1","modeNames","tonics","modeName","scaleChords","inScale","scaleNotes","exports","abcNotation","ChordType","collection","Core","key","Pcset","progression","ScaleType","hasOwnProperty","Tonal","PcSet","ChordDictionary","ScaleDictionary","k","defineProperty","enumerable","AbcNotation","Chord","Collection","Interval","Key","Midi","Mode","Note","Progression","Range","RomanNumeral","Scale","factory","require$$0","require$$1","require$$2","require$$3","require$$4","require$$5","require$$6","require$$7","require$$8","require$$9","require$$10","require$$11","require$$12","require$$13","require$$14","require$$15","require$$16"],"mappings":"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"} \ No newline at end of file diff --git a/packages/tonal/index.ts b/packages/tonal/index.ts index 1d8c9707..f9240713 100644 --- a/packages/tonal/index.ts +++ b/packages/tonal/index.ts @@ -1,41 +1,50 @@ -import * as AbcNotation from "@tonaljs/abc-notation"; +import AbcNotation from "@tonaljs/abc-notation"; import * as Array from "@tonaljs/array"; -import * as Chord from "@tonaljs/chord"; -import * as ChordDictionary from "@tonaljs/chord-dictionary"; +import Chord from "@tonaljs/chord"; +import ChordType from "@tonaljs/chord-type"; +import Collection from "@tonaljs/collection"; import * as Core from "@tonaljs/core"; -import * as Interval from "@tonaljs/interval"; -import * as Key from "@tonaljs/key"; -import * as Midi from "@tonaljs/midi"; -import * as Mode from "@tonaljs/mode"; -import * as Note from "@tonaljs/note"; -import * as PcSet from "@tonaljs/pcset"; -import * as Progression from "@tonaljs/progression"; -import * as Range from "@tonaljs/range"; -import * as RomanNumeral from "@tonaljs/roman-numeral"; -import * as Scale from "@tonaljs/scale"; -import * as ScaleDictionary from "@tonaljs/scale-dictionary"; +import Interval from "@tonaljs/interval"; +import Key from "@tonaljs/key"; +import Midi from "@tonaljs/midi"; +import Mode from "@tonaljs/mode"; +import Note from "@tonaljs/note"; +import Pcset from "@tonaljs/pcset"; +import Progression from "@tonaljs/progression"; +import Range from "@tonaljs/range"; +import RomanNumeral from "@tonaljs/roman-numeral"; +import Scale from "@tonaljs/scale"; +import ScaleType from "@tonaljs/scale-type"; export * from "@tonaljs/core"; -// backwards compatibility +// deprecated (backwards compatibility) const Tonal = Core; +const PcSet = Pcset; +const ChordDictionary = ChordType; +const ScaleDictionary = ScaleType; export { AbcNotation, Array, Chord, - ChordDictionary, + ChordType, + Collection, + Core, Note, Interval, Key, Midi, Mode, - PcSet, + Pcset, Progression, Range, RomanNumeral, Scale, - ScaleDictionary, - Core, - Tonal + ScaleType, + // backwards + Tonal, + PcSet, + ChordDictionary, + ScaleDictionary }; diff --git a/packages/tonal/package.json b/packages/tonal/package.json index b8b4b962..177b8f54 100644 --- a/packages/tonal/package.json +++ b/packages/tonal/package.json @@ -16,8 +16,9 @@ "dependencies": { "@tonaljs/abc-notation": "^3.2.4", "@tonaljs/array": "^3.2.4", + "@tonaljs/collection": "^3.2.4", "@tonaljs/chord": "^3.4.1", - "@tonaljs/chord-dictionary": "^3.4.1", + "@tonaljs/chord-type": "^3.4.1", "@tonaljs/core": "^3.3.0", "@tonaljs/interval": "^3.2.4", "@tonaljs/key": "^3.4.7", @@ -29,7 +30,7 @@ "@tonaljs/range": "^3.2.5", "@tonaljs/roman-numeral": "^3.3.2", "@tonaljs/scale": "^3.3.5", - "@tonaljs/scale-dictionary": "^3.4.3" + "@tonaljs/scale-type": "^3.4.3" }, "author": "danigb@gmail.com", "license": "MIT", diff --git a/packages/tonal/tonal.test.ts b/packages/tonal/tonal.test.ts index 439a36d6..3d9e7285 100644 --- a/packages/tonal/tonal.test.ts +++ b/packages/tonal/tonal.test.ts @@ -8,30 +8,37 @@ describe("@tonaljs/tonal", () => { "Array", "Chord", "ChordDictionary", + "ChordType", + "Collection", "Core", "Interval", "Key", "Midi", "Mode", "Note", - "PcSet", + "PcSet", // <- deprecated + "Pcset", "Progression", "Range", "RomanNumeral", "Scale", "ScaleDictionary", + "ScaleType", "Tonal", "accToAlt", "altToAcc", "coordToInterval", "coordToNote", "decode", + "deprecate", "distance", "encode", + "fillStr", "interval", "isNamed", "isPitch", "note", + "stepToLetter", "tokenizeInterval", "tokenizeNote", "transpose"