mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-11 21:58:37 +00:00
build
This commit is contained in:
parent
70dea049ae
commit
64e24e5fc1
File diff suppressed because it is too large
Load Diff
828
docs/_snowpack/pkg/chord-voicings.js
Normal file
828
docs/_snowpack/pkg/chord-voicings.js
Normal file
@ -0,0 +1,828 @@
|
||||
import { c as createCommonjsModule, a as commonjsGlobal, g as getDefaultExportFromCjs } from './common/_commonjsHelpers-8c19dec8.js';
|
||||
import { n as note, t as transpose$2, d as distance$1, m as modes, c as all, e as tokenizeNote, g as get$2, f as deprecate, h as isSupersetOf, j as all$1, k as isSubsetOf, l as get$3, o as interval, p as compact$1, q as toMidi, r as range$1, s as midiToNoteName, C as Core, u as index$5, v as index$1$1, w as index$1$2, x as index$6, y as index$7, b as index$8, z as index$9, A as index$a, B as index$1$3, a as index$b, D as index$c, i as index$d, E as accToAlt, F as altToAcc, G as coordToInterval, H as coordToNote, I as decode, J as encode, K as fillStr$1, L as isNamed, M as isPitch, N as stepToLetter, O as tokenizeInterval } from './common/index.es-d2606df1.js';
|
||||
|
||||
const fillStr = (character, times) => Array(times + 1).join(character);
|
||||
const REGEX = /^(_{1,}|=|\^{1,}|)([abcdefgABCDEFG])([,']*)$/;
|
||||
function tokenize(str) {
|
||||
const m = REGEX.exec(str);
|
||||
if (!m) {
|
||||
return ["", "", ""];
|
||||
}
|
||||
return [m[1], m[2], m[3]];
|
||||
}
|
||||
/**
|
||||
* Convert a (string) note in ABC notation into a (string) note in scientific notation
|
||||
*
|
||||
* @example
|
||||
* abcToScientificNotation("c") // => "C5"
|
||||
*/
|
||||
function abcToScientificNotation(str) {
|
||||
const [acc, letter, oct] = tokenize(str);
|
||||
if (letter === "") {
|
||||
return "";
|
||||
}
|
||||
let o = 4;
|
||||
for (let i = 0; i < oct.length; i++) {
|
||||
o += oct.charAt(i) === "," ? -1 : 1;
|
||||
}
|
||||
const a = acc[0] === "_"
|
||||
? acc.replace(/_/g, "b")
|
||||
: acc[0] === "^"
|
||||
? acc.replace(/\^/g, "#")
|
||||
: "";
|
||||
return letter.charCodeAt(0) > 96
|
||||
? letter.toUpperCase() + a + (o + 1)
|
||||
: letter + a + o;
|
||||
}
|
||||
/**
|
||||
* Convert a (string) note in scientific notation into a (string) note in ABC notation
|
||||
*
|
||||
* @example
|
||||
* scientificToAbcNotation("C#4") // => "^C"
|
||||
*/
|
||||
function scientificToAbcNotation(str) {
|
||||
const n = note(str);
|
||||
if (n.empty || (!n.oct && n.oct !== 0)) {
|
||||
return "";
|
||||
}
|
||||
const { letter, acc, oct } = n;
|
||||
const a = acc[0] === "b" ? acc.replace(/b/g, "_") : acc.replace(/#/g, "^");
|
||||
const l = oct > 4 ? letter.toLowerCase() : letter;
|
||||
const o = oct === 5 ? "" : oct > 4 ? fillStr("'", oct - 5) : fillStr(",", 4 - oct);
|
||||
return a + l + o;
|
||||
}
|
||||
function transpose(note, interval) {
|
||||
return scientificToAbcNotation(transpose$2(abcToScientificNotation(note), interval));
|
||||
}
|
||||
function distance(from, to) {
|
||||
return distance$1(abcToScientificNotation(from), abcToScientificNotation(to));
|
||||
}
|
||||
var index = {
|
||||
abcToScientificNotation,
|
||||
scientificToAbcNotation,
|
||||
tokenize,
|
||||
transpose,
|
||||
distance,
|
||||
};
|
||||
|
||||
// ascending range
|
||||
function ascR(b, n) {
|
||||
const a = [];
|
||||
// tslint:disable-next-line:curly
|
||||
for (; n--; a[n] = n + b)
|
||||
;
|
||||
return a;
|
||||
}
|
||||
// descending range
|
||||
function descR(b, n) {
|
||||
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<number>}
|
||||
*
|
||||
* @example
|
||||
* range(-2, 2) // => [-2, -1, 0, 1, 2]
|
||||
* range(2, -2) // => [2, 1, 0, -1, -2]
|
||||
*/
|
||||
function range(from, to) {
|
||||
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} array
|
||||
* @return {Array} the rotated array
|
||||
*
|
||||
* @example
|
||||
* rotate(1, [1, 2, 3]) // => [2, 3, 1]
|
||||
*/
|
||||
function rotate(times, arr) {
|
||||
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 array with the null values removed
|
||||
* @function
|
||||
* @param {Array} array
|
||||
* @return {Array}
|
||||
*
|
||||
* @example
|
||||
* compact(["a", "b", null, "c"]) // => ["a", "b", "c"]
|
||||
*/
|
||||
function compact(arr) {
|
||||
return arr.filter((n) => n === 0 || n);
|
||||
}
|
||||
/**
|
||||
* Sort an array of notes in ascending order. Pitch classes are listed
|
||||
* before notes. Any string that is not a note is removed.
|
||||
*
|
||||
* @param {string[]} notes
|
||||
* @return {string[]} sorted array of notes
|
||||
*
|
||||
* @example
|
||||
* 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']
|
||||
*/
|
||||
function sortedNoteNames(notes) {
|
||||
const valid = notes.map((n) => note(n)).filter((n) => !n.empty);
|
||||
return valid.sort((a, b) => a.height - b.height).map((n) => n.name);
|
||||
}
|
||||
/**
|
||||
* Get sorted notes with duplicates removed. Pitch classes are listed
|
||||
* before notes.
|
||||
*
|
||||
* @function
|
||||
* @param {string[]} array
|
||||
* @return {string[]} unique sorted notes
|
||||
*
|
||||
* @example
|
||||
* Array.sortedUniqNoteNames(['a', 'b', 'c2', '1p', 'p2', 'c2', 'b', 'c', 'c3' ])
|
||||
* // => [ 'C', 'A', 'B', 'C2', 'C3' ]
|
||||
*/
|
||||
function sortedUniqNoteNames(arr) {
|
||||
return sortedNoteNames(arr).filter((n, i, a) => i === 0 || n !== a[i - 1]);
|
||||
}
|
||||
/**
|
||||
* Randomizes the order of the specified array in-place, using the Fisher–Yates shuffle.
|
||||
*
|
||||
* @function
|
||||
* @param {Array} array
|
||||
* @return {Array} the array shuffled
|
||||
*
|
||||
* @example
|
||||
* shuffle(["C", "D", "E", "F"]) // => [...]
|
||||
*/
|
||||
function shuffle(arr, rnd = Math.random) {
|
||||
let i;
|
||||
let t;
|
||||
let m = 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 array
|
||||
*
|
||||
* @param {Array} array - the array
|
||||
* @return {Array<Array>} an array 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"]
|
||||
* ]
|
||||
*/
|
||||
function permutations(arr) {
|
||||
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;
|
||||
}));
|
||||
}, []);
|
||||
}
|
||||
|
||||
var index_es = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
compact: compact,
|
||||
permutations: permutations,
|
||||
range: range,
|
||||
rotate: rotate,
|
||||
shuffle: shuffle,
|
||||
sortedNoteNames: sortedNoteNames,
|
||||
sortedUniqNoteNames: sortedUniqNoteNames
|
||||
});
|
||||
|
||||
const namedSet = (notes) => {
|
||||
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) => pcToName[chroma];
|
||||
};
|
||||
function detect(source) {
|
||||
const notes = source.map((n) => note(n).pc).filter((x) => x);
|
||||
if (note.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const found = findExactMatches(notes, 1);
|
||||
return found
|
||||
.filter((chord) => chord.weight)
|
||||
.sort((a, b) => b.weight - a.weight)
|
||||
.map((chord) => chord.name);
|
||||
}
|
||||
function findExactMatches(notes, weight) {
|
||||
const tonic = notes[0];
|
||||
const tonicChroma = note(tonic).chroma;
|
||||
const noteName = namedSet(notes);
|
||||
// we need to test all chormas to get the correct baseNote
|
||||
const allModes = modes(notes, false);
|
||||
const found = [];
|
||||
allModes.forEach((mode, index) => {
|
||||
// some chords could have the same chroma but different interval spelling
|
||||
const chordTypes = all().filter((chordType) => chordType.chroma === mode);
|
||||
chordTypes.forEach((chordType) => {
|
||||
const chordName = chordType.aliases[0];
|
||||
const baseNote = noteName(index);
|
||||
const isInversion = index !== tonicChroma;
|
||||
if (isInversion) {
|
||||
found.push({
|
||||
weight: 0.5 * weight,
|
||||
name: `${baseNote}${chordName}/${tonic}`,
|
||||
});
|
||||
}
|
||||
else {
|
||||
found.push({ weight: 1 * weight, name: `${baseNote}${chordName}` });
|
||||
}
|
||||
});
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
const NoChord = {
|
||||
empty: true,
|
||||
name: "",
|
||||
symbol: "",
|
||||
root: "",
|
||||
rootDegree: 0,
|
||||
type: "",
|
||||
tonic: null,
|
||||
setNum: NaN,
|
||||
quality: "Unknown",
|
||||
chroma: "",
|
||||
normalized: "",
|
||||
aliases: [],
|
||||
notes: [],
|
||||
intervals: [],
|
||||
};
|
||||
// 6, 64, 7, 9, 11 and 13 are consider part of the chord
|
||||
// (see https://github.com/danigb/tonal/issues/55)
|
||||
const NUM_TYPES = /^(6|64|7|9|11|13)$/;
|
||||
/**
|
||||
* Tokenize a chord name. It returns an array with the tonic and chord type
|
||||
* If not tonic is found, all the name is considered the chord name.
|
||||
*
|
||||
* This function does NOT check if the chord type exists or not. It only tries
|
||||
* to split the tonic and chord type.
|
||||
*
|
||||
* @function
|
||||
* @param {string} name - the chord name
|
||||
* @return {Array} an array with [tonic, type]
|
||||
* @example
|
||||
* tokenize("Cmaj7") // => [ "C", "maj7" ]
|
||||
* tokenize("C7") // => [ "C", "7" ]
|
||||
* tokenize("mMaj7") // => [ null, "mMaj7" ]
|
||||
* tokenize("Cnonsense") // => [ null, "nonsense" ]
|
||||
*/
|
||||
function tokenize$1(name) {
|
||||
const [letter, acc, oct, type] = tokenizeNote(name);
|
||||
if (letter === "") {
|
||||
return ["", name];
|
||||
}
|
||||
// aug is augmented (see https://github.com/danigb/tonal/issues/55)
|
||||
if (letter === "A" && type === "ug") {
|
||||
return ["", "aug"];
|
||||
}
|
||||
// see: https://github.com/tonaljs/tonal/issues/70
|
||||
if (!type && (oct === "4" || oct === "5")) {
|
||||
return [letter + acc, oct];
|
||||
}
|
||||
if (NUM_TYPES.test(oct)) {
|
||||
return [letter + acc, oct + type];
|
||||
}
|
||||
else {
|
||||
return [letter + acc + oct, type];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get a Chord from a chord name.
|
||||
*/
|
||||
function get(src) {
|
||||
if (src === "") {
|
||||
return NoChord;
|
||||
}
|
||||
if (Array.isArray(src) && src.length === 2) {
|
||||
return getChord(src[1], src[0]);
|
||||
}
|
||||
else {
|
||||
const [tonic, type] = tokenize$1(src);
|
||||
const chord = getChord(type, tonic);
|
||||
return chord.empty ? getChord(src) : chord;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get chord properties
|
||||
*
|
||||
* @param typeName - the chord type name
|
||||
* @param [tonic] - Optional tonic
|
||||
* @param [root] - Optional root (requires a tonic)
|
||||
*/
|
||||
function getChord(typeName, optionalTonic, optionalRoot) {
|
||||
const type = get$2(typeName);
|
||||
const tonic = note(optionalTonic || "");
|
||||
const root = note(optionalRoot || "");
|
||||
if (type.empty ||
|
||||
(optionalTonic && tonic.empty) ||
|
||||
(optionalRoot && root.empty)) {
|
||||
return NoChord;
|
||||
}
|
||||
const rootInterval = distance$1(tonic.pc, root.pc);
|
||||
const rootDegree = type.intervals.indexOf(rootInterval) + 1;
|
||||
if (!root.empty && !rootDegree) {
|
||||
return NoChord;
|
||||
}
|
||||
const intervals = Array.from(type.intervals);
|
||||
for (let i = 1; i < rootDegree; i++) {
|
||||
const num = intervals[0][0];
|
||||
const quality = intervals[0][1];
|
||||
const newNum = parseInt(num, 10) + 7;
|
||||
intervals.push(`${newNum}${quality}`);
|
||||
intervals.shift();
|
||||
}
|
||||
const notes = tonic.empty
|
||||
? []
|
||||
: intervals.map((i) => transpose$2(tonic, i));
|
||||
typeName = type.aliases.indexOf(typeName) !== -1 ? typeName : type.aliases[0];
|
||||
const symbol = `${tonic.empty ? "" : tonic.pc}${typeName}${root.empty || rootDegree <= 1 ? "" : "/" + root.pc}`;
|
||||
const name = `${optionalTonic ? tonic.pc + " " : ""}${type.name}${rootDegree > 1 && optionalRoot ? " over " + root.pc : ""}`;
|
||||
return {
|
||||
...type,
|
||||
name,
|
||||
symbol,
|
||||
type: type.name,
|
||||
root: root.name,
|
||||
intervals,
|
||||
rootDegree,
|
||||
tonic: tonic.name,
|
||||
notes,
|
||||
};
|
||||
}
|
||||
const chord = deprecate("Chord.chord", "Chord.get", get);
|
||||
/**
|
||||
* Transpose a chord name
|
||||
*
|
||||
* @param {string} chordName - the chord name
|
||||
* @return {string} the transposed chord
|
||||
*
|
||||
* @example
|
||||
* transpose('Dm7', 'P4') // => 'Gm7
|
||||
*/
|
||||
function transpose$1(chordName, interval) {
|
||||
const [tonic, type] = tokenize$1(chordName);
|
||||
if (!tonic) {
|
||||
return chordName;
|
||||
}
|
||||
return transpose$2(tonic, interval) + type;
|
||||
}
|
||||
/**
|
||||
* Get all scales where the given chord fits
|
||||
*
|
||||
* @example
|
||||
* chordScales('C7b9')
|
||||
* // => ["phrygian dominant", "flamenco", "spanish heptatonic", "half-whole diminished", "chromatic"]
|
||||
*/
|
||||
function chordScales(name) {
|
||||
const s = get(name);
|
||||
const isChordIncluded = isSupersetOf(s.chroma);
|
||||
return all$1()
|
||||
.filter((scale) => isChordIncluded(scale.chroma))
|
||||
.map((scale) => scale.name);
|
||||
}
|
||||
/**
|
||||
* Get all chords names that are a superset of the given one
|
||||
* (has the same notes and at least one more)
|
||||
*
|
||||
* @function
|
||||
* @example
|
||||
* extended("CMaj7")
|
||||
* // => [ 'Cmaj#4', 'Cmaj7#9#11', 'Cmaj9', 'CM7add13', 'Cmaj13', 'Cmaj9#11', 'CM13#11', 'CM7b9' ]
|
||||
*/
|
||||
function extended(chordName) {
|
||||
const s = get(chordName);
|
||||
const isSuperset = isSupersetOf(s.chroma);
|
||||
return all()
|
||||
.filter((chord) => isSuperset(chord.chroma))
|
||||
.map((chord) => s.tonic + chord.aliases[0]);
|
||||
}
|
||||
/**
|
||||
* Find all chords names that are a subset of the given one
|
||||
* (has less notes but all from the given chord)
|
||||
*
|
||||
* @example
|
||||
*/
|
||||
function reduced(chordName) {
|
||||
const s = get(chordName);
|
||||
const isSubset = isSubsetOf(s.chroma);
|
||||
return all()
|
||||
.filter((chord) => isSubset(chord.chroma))
|
||||
.map((chord) => s.tonic + chord.aliases[0]);
|
||||
}
|
||||
var index$1 = {
|
||||
getChord,
|
||||
get,
|
||||
detect,
|
||||
chordScales,
|
||||
extended,
|
||||
reduced,
|
||||
tokenize: tokenize$1,
|
||||
transpose: transpose$1,
|
||||
// deprecate
|
||||
chord,
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a tonic and a chord list expressed with roman numeral notation
|
||||
* returns the progression expressed with leadsheet chords symbols notation
|
||||
* @example
|
||||
* fromRomanNumerals("C", ["I", "IIm7", "V7"]);
|
||||
* // => ["C", "Dm7", "G7"]
|
||||
*/
|
||||
function fromRomanNumerals(tonic, chords) {
|
||||
const romanNumerals = chords.map(get$3);
|
||||
return romanNumerals.map((rn) => transpose$2(tonic, interval(rn)) + rn.chordType);
|
||||
}
|
||||
/**
|
||||
* Given a tonic and a chord list with leadsheet symbols notation,
|
||||
* return the chord list with roman numeral notation
|
||||
* @example
|
||||
* toRomanNumerals("C", ["CMaj7", "Dm7", "G7"]);
|
||||
* // => ["IMaj7", "IIm7", "V7"]
|
||||
*/
|
||||
function toRomanNumerals(tonic, chords) {
|
||||
return chords.map((chord) => {
|
||||
const [note, chordType] = tokenize$1(chord);
|
||||
const intervalName = distance$1(tonic, note);
|
||||
const roman = get$3(interval(intervalName));
|
||||
return roman.name + chordType;
|
||||
});
|
||||
}
|
||||
var index$2 = { fromRomanNumerals, toRomanNumerals };
|
||||
|
||||
/**
|
||||
* Create a numeric range. You supply a list of notes or numbers and it will
|
||||
* be connected to create complex ranges.
|
||||
*
|
||||
* @param {Array} notes - the list of notes or midi numbers used
|
||||
* @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 ]
|
||||
* // it works midi notes
|
||||
* numeric([10, 5]) // => [ 10, 9, 8, 7, 6, 5 ]
|
||||
* // complex range
|
||||
* numeric(["C4", "E4", "Bb3"]) // => [60, 61, 62, 63, 64, 63, 62, 61, 60, 59, 58]
|
||||
*/
|
||||
function numeric(notes) {
|
||||
const midi = compact$1(notes.map(toMidi));
|
||||
if (!notes.length || midi.length !== notes.length) {
|
||||
// there is no valid notes
|
||||
return [];
|
||||
}
|
||||
return midi.reduce((result, note) => {
|
||||
const last = result[result.length - 1];
|
||||
return result.concat(range$1(last, note).slice(1));
|
||||
}, [midi[0]]);
|
||||
}
|
||||
/**
|
||||
* Create a range of chromatic notes. The altered notes will use flats.
|
||||
*
|
||||
* @function
|
||||
* @param {Array} notes - the list of notes or midi note numbers to create a range from
|
||||
* @param {Object} options - The same as `midiToNoteName` (`{ sharps: boolean, pitchClass: boolean }`)
|
||||
* @return {Array} an array of note names
|
||||
*
|
||||
* @example
|
||||
* Range.chromatic(["C2, "E2", "D2"]) // => ["C2", "Db2", "D2", "Eb2", "E2", "Eb2", "D2"]
|
||||
* // with sharps
|
||||
* Range.chromatic(["C2", "C3"], { sharps: true }) // => [ "C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2", "C3" ]
|
||||
*/
|
||||
function chromatic(notes, options) {
|
||||
return numeric(notes).map((midi) => midiToNoteName(midi, options));
|
||||
}
|
||||
var index$3 = { numeric, chromatic };
|
||||
|
||||
// CONSTANTS
|
||||
const NONE = {
|
||||
empty: true,
|
||||
name: "",
|
||||
upper: undefined,
|
||||
lower: undefined,
|
||||
type: undefined,
|
||||
additive: [],
|
||||
};
|
||||
const NAMES = ["4/4", "3/4", "2/4", "2/2", "12/8", "9/8", "6/8", "3/8"];
|
||||
// PUBLIC API
|
||||
function names() {
|
||||
return NAMES.slice();
|
||||
}
|
||||
const REGEX$1 = /^(\d?\d(?:\+\d)*)\/(\d)$/;
|
||||
const CACHE = new Map();
|
||||
function get$1(literal) {
|
||||
const cached = CACHE.get(literal);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
const ts = build(parse(literal));
|
||||
CACHE.set(literal, ts);
|
||||
return ts;
|
||||
}
|
||||
function parse(literal) {
|
||||
if (typeof literal === "string") {
|
||||
const [_, up, low] = REGEX$1.exec(literal) || [];
|
||||
return parse([up, low]);
|
||||
}
|
||||
const [up, down] = literal;
|
||||
const denominator = +down;
|
||||
if (typeof up === "number") {
|
||||
return [up, denominator];
|
||||
}
|
||||
const list = up.split("+").map((n) => +n);
|
||||
return list.length === 1 ? [list[0], denominator] : [list, denominator];
|
||||
}
|
||||
var index$4 = { names, parse, get: get$1 };
|
||||
// PRIVATE
|
||||
function build([up, down]) {
|
||||
const upper = Array.isArray(up) ? up.reduce((a, b) => a + b, 0) : up;
|
||||
const lower = down;
|
||||
if (upper === 0 || lower === 0) {
|
||||
return NONE;
|
||||
}
|
||||
const name = Array.isArray(up) ? `${up.join("+")}/${down}` : `${up}/${down}`;
|
||||
const additive = Array.isArray(up) ? up : [];
|
||||
const type = lower === 4 || lower === 2
|
||||
? "simple"
|
||||
: lower === 8 && upper % 3 === 0
|
||||
? "compound"
|
||||
: "irregular";
|
||||
return {
|
||||
empty: false,
|
||||
name,
|
||||
type,
|
||||
upper,
|
||||
lower,
|
||||
additive,
|
||||
};
|
||||
}
|
||||
|
||||
// deprecated (backwards compatibility)
|
||||
const Tonal = Core;
|
||||
const PcSet = index$5;
|
||||
const ChordDictionary = index$1$1;
|
||||
const ScaleDictionary = index$1$2;
|
||||
|
||||
var index_es$1 = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
Array: index_es,
|
||||
Core: Core,
|
||||
ChordDictionary: ChordDictionary,
|
||||
PcSet: PcSet,
|
||||
ScaleDictionary: ScaleDictionary,
|
||||
Tonal: Tonal,
|
||||
AbcNotation: index,
|
||||
Chord: index$1,
|
||||
ChordType: index$1$1,
|
||||
Collection: index$6,
|
||||
DurationValue: index$7,
|
||||
Interval: index$8,
|
||||
Key: index$9,
|
||||
Midi: index$a,
|
||||
Mode: index$1$3,
|
||||
Note: index$b,
|
||||
Pcset: index$5,
|
||||
Progression: index$2,
|
||||
Range: index$3,
|
||||
RomanNumeral: index$c,
|
||||
Scale: index$d,
|
||||
ScaleType: index$1$2,
|
||||
TimeSignature: index$4,
|
||||
accToAlt: accToAlt,
|
||||
altToAcc: altToAcc,
|
||||
coordToInterval: coordToInterval,
|
||||
coordToNote: coordToNote,
|
||||
decode: decode,
|
||||
deprecate: deprecate,
|
||||
distance: distance$1,
|
||||
encode: encode,
|
||||
fillStr: fillStr$1,
|
||||
interval: interval,
|
||||
isNamed: isNamed,
|
||||
isPitch: isPitch,
|
||||
note: note,
|
||||
stepToLetter: stepToLetter,
|
||||
tokenizeInterval: tokenizeInterval,
|
||||
tokenizeNote: tokenizeNote,
|
||||
transpose: transpose$2
|
||||
});
|
||||
|
||||
var getBestVoicing_1 = createCommonjsModule(function (module, exports) {
|
||||
exports.__esModule = true;
|
||||
exports.getBestVoicing = void 0;
|
||||
function getBestVoicing(voicingOptions) {
|
||||
var chord = voicingOptions.chord, range = voicingOptions.range, finder = voicingOptions.finder, picker = voicingOptions.picker, lastVoicing = voicingOptions.lastVoicing;
|
||||
var voicings = finder(chord, range);
|
||||
if (!voicings.length) {
|
||||
return [];
|
||||
}
|
||||
return picker(voicings, lastVoicing);
|
||||
}
|
||||
exports.getBestVoicing = getBestVoicing;
|
||||
|
||||
});
|
||||
|
||||
var tokenizeChord_1 = createCommonjsModule(function (module, exports) {
|
||||
exports.__esModule = true;
|
||||
exports.tokenizeChord = void 0;
|
||||
function tokenizeChord(chord) {
|
||||
var match = (chord || '').match(/^([A-G][b#]*)([^\/]*)[\/]?([A-G][b#]*)?$/);
|
||||
if (!match) {
|
||||
// console.warn('could not tokenize chord', chord);
|
||||
return [];
|
||||
}
|
||||
return match.slice(1);
|
||||
}
|
||||
exports.tokenizeChord = tokenizeChord;
|
||||
|
||||
});
|
||||
|
||||
var voicingsInRange_1 = createCommonjsModule(function (module, exports) {
|
||||
exports.__esModule = true;
|
||||
exports.voicingsInRange = void 0;
|
||||
|
||||
|
||||
|
||||
function voicingsInRange(chord, dictionary, range) {
|
||||
if (dictionary === void 0) { dictionary = dictionaryVoicing_1.lefthand; }
|
||||
if (range === void 0) { range = ['D3', 'A4']; }
|
||||
var _a = (0, tokenizeChord_1.tokenizeChord)(chord), tonic = _a[0], symbol = _a[1];
|
||||
if (!dictionary[symbol]) {
|
||||
return [];
|
||||
}
|
||||
// resolve array of interval arrays for the wanted symbol
|
||||
var voicings = dictionary[symbol].map(function (intervals) { return intervals.split(' '); });
|
||||
var notesInRange = index_es$1.Range.chromatic(range); // gives array of notes inside range
|
||||
return voicings.reduce(function (voiced, voicing) {
|
||||
// transpose intervals relative to first interval (e.g. 3m 5P > 1P 3M)
|
||||
var relativeIntervals = voicing.map(function (interval) { return index_es$1.Interval.substract(interval, voicing[0]); });
|
||||
// get enharmonic correct pitch class the bottom note
|
||||
var bottomPitchClass = index_es$1.Note.transpose(tonic, voicing[0]);
|
||||
// get all possible start notes for voicing
|
||||
var starts = notesInRange
|
||||
// only get the start notes:
|
||||
.filter(function (note) { return index_es$1.Note.chroma(note) === index_es$1.Note.chroma(bottomPitchClass); })
|
||||
// filter out start notes that will overshoot the top end of the range
|
||||
.filter(function (note) {
|
||||
return index_es$1.Note.midi(index_es$1.Note.transpose(note, relativeIntervals[relativeIntervals.length - 1])) <= index_es$1.Note.midi(range[1]);
|
||||
})
|
||||
// replace Range.chromatic notes with the correct enharmonic equivalents
|
||||
.map(function (note) { return index_es$1.Note.enharmonic(note, bottomPitchClass); });
|
||||
// render one voicing for each start note
|
||||
var notes = starts.map(function (start) { return relativeIntervals.map(function (interval) { return index_es$1.Note.transpose(start, interval); }); });
|
||||
return voiced.concat(notes);
|
||||
}, []);
|
||||
}
|
||||
exports.voicingsInRange = voicingsInRange;
|
||||
|
||||
});
|
||||
|
||||
var dictionaryVoicing_1 = createCommonjsModule(function (module, exports) {
|
||||
var __assign = (commonjsGlobal && commonjsGlobal.__assign) || function () {
|
||||
__assign = Object.assign || function(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
var __rest = (commonjsGlobal && commonjsGlobal.__rest) || function (s, e) {
|
||||
var t = {};
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||
t[p] = s[p];
|
||||
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
||||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
||||
t[p[i]] = s[p[i]];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
exports.__esModule = true;
|
||||
exports.dictionaryVoicing = exports.dictionaryVoicingFinder = exports.triads = exports.guidetones = exports.lefthand = void 0;
|
||||
|
||||
|
||||
exports.lefthand = {
|
||||
m7: ['3m 5P 7m 9M', '7m 9M 10m 12P'],
|
||||
'7': ['3M 6M 7m 9M', '7m 9M 10M 13M'],
|
||||
'^7': ['3M 5P 7M 9M', '7M 9M 10M 12P'],
|
||||
'69': ['3M 5P 6A 9M'],
|
||||
m7b5: ['3m 5d 7m 8P', '7m 8P 10m 12d'],
|
||||
'7b9': ['3M 6m 7m 9m', '7m 9m 10M 13m'],
|
||||
'7b13': ['3M 6m 7m 9m', '7m 9m 10M 13m'],
|
||||
o7: ['1P 3m 5d 6M', '5d 6M 8P 10m'],
|
||||
'7#11': ['7m 9M 11A 13A'],
|
||||
'7#9': ['3M 7m 9A'],
|
||||
mM7: ['3m 5P 7M 9M', '7M 9M 10m 12P'],
|
||||
m6: ['3m 5P 6M 9M', '6M 9M 10m 12P']
|
||||
};
|
||||
exports.guidetones = {
|
||||
m7: ['3m 7m', '7m 10m'],
|
||||
m9: ['3m 7m', '7m 10m'],
|
||||
'7': ['3M 7m', '7m 10M'],
|
||||
'^7': ['3M 7M', '7M 10M'],
|
||||
'^9': ['3M 7M', '7M 10M'],
|
||||
'69': ['3M 6M'],
|
||||
'6': ['3M 6M', '6M 10M'],
|
||||
m7b5: ['3m 7m', '7m 10m'],
|
||||
'7b9': ['3M 7m', '7m 10M'],
|
||||
'7b13': ['3M 7m', '7m 10M'],
|
||||
o7: ['3m 6M', '6M 10m'],
|
||||
'7#11': ['3M 7m', '7m 10M'],
|
||||
'7#9': ['3M 7m', '7m 10M'],
|
||||
mM7: ['3m 7M', '7M 10m'],
|
||||
m6: ['3m 6M', '6M 10m']
|
||||
};
|
||||
exports.triads = {
|
||||
M: ['1P 3M 5P', '3M 5P 8P', '5P 8P 10M'],
|
||||
m: ['1P 3m 5P', '3m 5P 8P', '5P 8P 10m'],
|
||||
o: ['1P 3m 5d', '3m 5d 8P', '5d 8P 10m'],
|
||||
aug: ['1P 3m 5A', '3m 5A 8P', '5A 8P 10m']
|
||||
};
|
||||
var dictionaryVoicingFinder = function (dictionary) { return function (chordSymbol, range) {
|
||||
return (0, voicingsInRange_1.voicingsInRange)(chordSymbol, dictionary, range);
|
||||
}; };
|
||||
exports.dictionaryVoicingFinder = dictionaryVoicingFinder;
|
||||
var dictionaryVoicing = function (props) {
|
||||
var dictionary = props.dictionary, range = props.range, rest = __rest(props, ["dictionary", "range"]);
|
||||
return (0, getBestVoicing_1.getBestVoicing)(__assign(__assign({}, rest), { range: range, finder: (0, exports.dictionaryVoicingFinder)(dictionary) }));
|
||||
};
|
||||
exports.dictionaryVoicing = dictionaryVoicing;
|
||||
|
||||
});
|
||||
|
||||
var minTopNoteDiff_1 = createCommonjsModule(function (module, exports) {
|
||||
exports.__esModule = true;
|
||||
exports.minTopNoteDiff = void 0;
|
||||
|
||||
function minTopNoteDiff(voicings, lastVoicing) {
|
||||
if (!lastVoicing) {
|
||||
return voicings[0];
|
||||
}
|
||||
var diff = function (voicing) {
|
||||
return Math.abs(index_es$1.Note.midi(lastVoicing[lastVoicing.length - 1]) - index_es$1.Note.midi(voicing[voicing.length - 1]));
|
||||
};
|
||||
return voicings.reduce(function (best, current) { return (diff(current) < diff(best) ? current : best); }, voicings[0]);
|
||||
}
|
||||
exports.minTopNoteDiff = minTopNoteDiff;
|
||||
|
||||
});
|
||||
|
||||
var dist = createCommonjsModule(function (module, exports) {
|
||||
exports.__esModule = true;
|
||||
|
||||
|
||||
|
||||
|
||||
exports["default"] = {
|
||||
tokenizeChord: tokenizeChord_1.tokenizeChord,
|
||||
getBestVoicing: getBestVoicing_1.getBestVoicing,
|
||||
dictionaryVoicing: dictionaryVoicing_1.dictionaryVoicing,
|
||||
dictionaryVoicingFinder: dictionaryVoicing_1.dictionaryVoicingFinder,
|
||||
lefthand: dictionaryVoicing_1.lefthand,
|
||||
guidetones: dictionaryVoicing_1.guidetones,
|
||||
triads: dictionaryVoicing_1.triads,
|
||||
minTopNoteDiff: minTopNoteDiff_1.minTopNoteDiff
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
var __pika_web_default_export_for_treeshaking__ = /*@__PURE__*/getDefaultExportFromCjs(dist);
|
||||
|
||||
export default __pika_web_default_export_for_treeshaking__;
|
||||
2364
docs/_snowpack/pkg/common/index.es-d2606df1.js
Normal file
2364
docs/_snowpack/pkg/common/index.es-d2606df1.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,7 @@
|
||||
{
|
||||
"imports": {
|
||||
"@tonaljs/tonal": "./@tonaljs/tonal.js",
|
||||
"chord-voicings": "./chord-voicings.js",
|
||||
"codemirror/lib/codemirror.css": "./codemirror/lib/codemirror.css",
|
||||
"codemirror/mode/javascript/javascript.js": "./codemirror/mode/javascript/javascript.js",
|
||||
"codemirror/mode/pegjs/pegjs.js": "./codemirror/mode/pegjs/pegjs.js",
|
||||
|
||||
1
docs/dist/parse.js
vendored
1
docs/dist/parse.js
vendored
@ -3,6 +3,7 @@ import * as strudel from "../_snowpack/link/strudel.js";
|
||||
import {Scale, Note, Interval} from "../_snowpack/pkg/@tonaljs/tonal.js";
|
||||
import "./tone.js";
|
||||
import "./midi.js";
|
||||
import "./voicings.js";
|
||||
import * as toneStuff from "./tone.js";
|
||||
import shapeshifter from "./shapeshifter.js";
|
||||
const {
|
||||
|
||||
18
docs/dist/voicings.js
vendored
Normal file
18
docs/dist/voicings.js
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
import {Pattern as _Pattern, stack, Hap} from "../_snowpack/link/strudel.js";
|
||||
import voicings from "../_snowpack/pkg/chord-voicings.js";
|
||||
const {dictionaryVoicing, minTopNoteDiff, lefthand} = voicings;
|
||||
const getVoicing = (chord, lastVoicing, range = ["F3", "A4"]) => dictionaryVoicing({
|
||||
chord,
|
||||
dictionary: lefthand,
|
||||
range,
|
||||
picker: minTopNoteDiff,
|
||||
lastVoicing
|
||||
});
|
||||
const Pattern = _Pattern;
|
||||
Pattern.prototype.voicings = function(range = ["F3", "A4"]) {
|
||||
let lastVoicing;
|
||||
return new Pattern((span) => this.query(span).map((event) => {
|
||||
lastVoicing = getVoicing(event.value, lastVoicing, range);
|
||||
return stack(...lastVoicing).query(span).map((hap) => new Hap(event.whole, event.part, hap.value));
|
||||
}).flat());
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user