@pepperhorn/chordl-guitar 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/chordLookup.d.ts +18 -0
- package/dist/chordLookup.js +214 -0
- package/dist/chordLookup.js.map +1 -0
- package/dist/generated/guitar.slim.json +1 -0
- package/dist/generated/ukulele.slim.json +1 -0
- package/dist/generatedShapes.d.ts +61 -0
- package/dist/generatedShapes.js +212 -0
- package/dist/generatedShapes.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/instruments.d.ts +66 -0
- package/dist/instruments.js +114 -0
- package/dist/instruments.js.map +1 -0
- package/dist/pitch.d.ts +33 -0
- package/dist/pitch.js +57 -0
- package/dist/pitch.js.map +1 -0
- package/dist/powerChords.d.ts +27 -0
- package/dist/powerChords.js +41 -0
- package/dist/powerChords.js.map +1 -0
- package/dist/staticPresets.d.ts +16 -0
- package/dist/staticPresets.js +48 -0
- package/dist/staticPresets.js.map +1 -0
- package/dist/voicingFacts.d.ts +48 -0
- package/dist/voicingFacts.js +74 -0
- package/dist/voicingFacts.js.map +1 -0
- package/dist/voicingSelect.d.ts +92 -0
- package/dist/voicingSelect.js +151 -0
- package/dist/voicingSelect.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { INSTRUMENTS, dbPositionToChord } from "./instruments.js";
|
|
2
|
+
const ROOT_STRING_INDEX = { E: 0, A: 1 };
|
|
3
|
+
/**
|
|
4
|
+
* Build a chords-db-shaped position for a power chord.
|
|
5
|
+
*
|
|
6
|
+
* @param rootPc - root pitch class, 0 = C
|
|
7
|
+
* @returns null when rootPc is out of range
|
|
8
|
+
*/
|
|
9
|
+
export function powerChordPosition(rootPc, opts) {
|
|
10
|
+
if (!Number.isInteger(rootPc) || rootPc < 0 || rootPc > 11)
|
|
11
|
+
return null;
|
|
12
|
+
const openMidi = INSTRUMENTS.guitar.openMidi;
|
|
13
|
+
const rootIdx = ROOT_STRING_INDEX[opts.stringSet];
|
|
14
|
+
const includeOctave = opts.includeOctave ?? true;
|
|
15
|
+
// Fret on the root string that sounds rootPc.
|
|
16
|
+
const fret = (rootPc - (openMidi[rootIdx] % 12) + 12) % 12;
|
|
17
|
+
// Open position keeps baseFret at the nut so the diagram shows the nut;
|
|
18
|
+
// elsewhere the window starts at the root fret and frets are window-relative.
|
|
19
|
+
const baseFret = fret === 0 ? 1 : fret;
|
|
20
|
+
const rootFret = fret === 0 ? 0 : 1;
|
|
21
|
+
const upperFret = fret === 0 ? 2 : 3;
|
|
22
|
+
const frets = new Array(6).fill(-1);
|
|
23
|
+
const fingers = new Array(6).fill(0);
|
|
24
|
+
frets[rootIdx] = rootFret;
|
|
25
|
+
fingers[rootIdx] = rootFret === 0 ? 0 : 1;
|
|
26
|
+
frets[rootIdx + 1] = upperFret;
|
|
27
|
+
fingers[rootIdx + 1] = fret === 0 ? 2 : 3;
|
|
28
|
+
if (includeOctave) {
|
|
29
|
+
frets[rootIdx + 2] = upperFret;
|
|
30
|
+
fingers[rootIdx + 2] = fret === 0 ? 3 : 4;
|
|
31
|
+
}
|
|
32
|
+
return { frets, fingers, baseFret, barres: [] };
|
|
33
|
+
}
|
|
34
|
+
/** The same shape as an svguitar Chord, ready to render. */
|
|
35
|
+
export function powerChordShape(rootPc, opts) {
|
|
36
|
+
const pos = powerChordPosition(rootPc, opts);
|
|
37
|
+
if (!pos)
|
|
38
|
+
return null;
|
|
39
|
+
return dbPositionToChord(pos, INSTRUMENTS.guitar.strings, opts.title);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=powerChords.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"powerChords.js","sourceRoot":"","sources":["../src/powerChords.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAYlE,MAAM,iBAAiB,GAAwC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAc,EACd,IAAuB;IAEvB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,EAAE;QAAE,OAAO,IAAI,CAAC;IAExE,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC7C,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC;IAEjD,8CAA8C;IAC9C,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;IAE3D,wEAAwE;IACxE,8EAA8E;IAC9E,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAErC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,KAAK,CAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE7C,KAAK,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC;IAC1B,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC;IAC/B,OAAO,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,IAAI,aAAa,EAAE,CAAC;QAClB,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC;QAC/B,OAAO,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAClD,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,IAAuB;IACrE,MAAM,GAAG,GAAG,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,iBAAiB,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;AACxE,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Chord } from "svguitar";
|
|
2
|
+
export interface StaticPreset {
|
|
3
|
+
key: string;
|
|
4
|
+
suffix: string;
|
|
5
|
+
chord: Chord;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Hand-authored beginner voicings on the top three strings (G B E).
|
|
9
|
+
*
|
|
10
|
+
* chords-db has no three-string voicings, so these cannot be derived — the table
|
|
11
|
+
* is the sole source. Every entry is checked against pitch-class arithmetic by
|
|
12
|
+
* staticPresets.test.ts, because a wrong shape is unrecoverable once printed.
|
|
13
|
+
*/
|
|
14
|
+
export declare const GUITAR_TOP3_PRESETS: StaticPreset[];
|
|
15
|
+
/** Look up a top-3 shape. Returns null rather than guessing at an unknown chord. */
|
|
16
|
+
export declare function lookupTop3Chord(key: string, suffix: string): Chord | null;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Strings 4-6 are always muted for top-3 voicings. */
|
|
2
|
+
const MUTED_LOW = [[4, "x"], [5, "x"], [6, "x"]];
|
|
3
|
+
function top3(key, suffix, g, b, e) {
|
|
4
|
+
const finger = (s, [fret, label]) => label ? [s, fret, label] : [s, fret];
|
|
5
|
+
return {
|
|
6
|
+
key,
|
|
7
|
+
suffix,
|
|
8
|
+
chord: {
|
|
9
|
+
fingers: [finger(3, g), finger(2, b), finger(1, e), ...MUTED_LOW],
|
|
10
|
+
barres: [],
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Hand-authored beginner voicings on the top three strings (G B E).
|
|
16
|
+
*
|
|
17
|
+
* chords-db has no three-string voicings, so these cannot be derived — the table
|
|
18
|
+
* is the sole source. Every entry is checked against pitch-class arithmetic by
|
|
19
|
+
* staticPresets.test.ts, because a wrong shape is unrecoverable once printed.
|
|
20
|
+
*/
|
|
21
|
+
export const GUITAR_TOP3_PRESETS = [
|
|
22
|
+
top3("C", "major", [0], [1, "1"], [0]),
|
|
23
|
+
top3("G", "major", [0], [0], [3, "3"]),
|
|
24
|
+
top3("D", "major", [2, "1"], [3, "3"], [2, "2"]),
|
|
25
|
+
top3("A", "major", [2, "1"], [2, "2"], [0]),
|
|
26
|
+
top3("E", "minor", [0], [0], [0]),
|
|
27
|
+
top3("A", "minor", [2, "2"], [1, "1"], [0]),
|
|
28
|
+
top3("D", "minor", [2, "2"], [3, "3"], [1, "1"]),
|
|
29
|
+
top3("E", "major", [1, "1"], [0], [0]),
|
|
30
|
+
top3("C", "7", [3, "3"], [1, "1"], [0]),
|
|
31
|
+
top3("G", "7", [0], [0], [1, "1"]),
|
|
32
|
+
top3("B", "minor", [4, "3"], [3, "2"], [2, "1"]),
|
|
33
|
+
top3("F", "major", [2, "2"], [1, "1"], [1, "1"]),
|
|
34
|
+
top3("D", "7", [2, "2"], [1, "1"], [2, "3"]),
|
|
35
|
+
top3("E", "7", [1, "1"], [3, "3"], [0]),
|
|
36
|
+
top3("A", "7", [0], [2, "2"], [0]),
|
|
37
|
+
top3("A", "m7", [0], [1, "1"], [0]),
|
|
38
|
+
top3("D", "m7", [2, "2"], [1, "1"], [1, "1"]),
|
|
39
|
+
top3("E", "m7", [0], [3, "3"], [0]),
|
|
40
|
+
top3("F", "maj7", [2, "2"], [1, "1"], [0]),
|
|
41
|
+
top3("G", "minor", [3, "1"], [3, "2"], [3, "3"]),
|
|
42
|
+
];
|
|
43
|
+
/** Look up a top-3 shape. Returns null rather than guessing at an unknown chord. */
|
|
44
|
+
export function lookupTop3Chord(key, suffix) {
|
|
45
|
+
const hit = GUITAR_TOP3_PRESETS.find((p) => p.key === key && p.suffix === suffix);
|
|
46
|
+
return hit ? hit.chord : null;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=staticPresets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staticPresets.js","sourceRoot":"","sources":["../src/staticPresets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Facts about a chord shape, derived from its sounding pitches.
|
|
3
|
+
*
|
|
4
|
+
* This module states facts; it never decides which shape a product should
|
|
5
|
+
* display. Selection lives in src/voicingSelect.ts.
|
|
6
|
+
*/
|
|
7
|
+
import type { ChordsDbPosition } from "./instruments.js";
|
|
8
|
+
export type Inversion = "root" | "1st" | "2nd" | "3rd" | "other";
|
|
9
|
+
export interface PositionFacts {
|
|
10
|
+
/** Sounding pitches, chords-db string order. */
|
|
11
|
+
midi: number[];
|
|
12
|
+
/** Lowest sounding pitch, or -1 for an all-muted position. */
|
|
13
|
+
bassMidi: number;
|
|
14
|
+
/** Pitch class of the bass note, or -1 when nothing sounds. */
|
|
15
|
+
bassPitchClass: number;
|
|
16
|
+
inversion: Inversion;
|
|
17
|
+
/** Sounding string count, including doublings. */
|
|
18
|
+
noteCount: number;
|
|
19
|
+
/** Distinct pitch classes, ascending. */
|
|
20
|
+
pitchClasses: number[];
|
|
21
|
+
/** Sounding strings beyond the first of each pitch class. */
|
|
22
|
+
doubledCount: number;
|
|
23
|
+
hasBarre: boolean;
|
|
24
|
+
/** Barre-free and at the nut — the beginner "open chord" rung. */
|
|
25
|
+
isOpenShape: boolean;
|
|
26
|
+
baseFret: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Derive facts for one position.
|
|
30
|
+
*
|
|
31
|
+
* @param pos - chords-db position
|
|
32
|
+
* @param openMidi - open-string MIDI in chords-db index order
|
|
33
|
+
* @param rootPc - root pitch class from `rootPitchClass(label)`, or null when
|
|
34
|
+
* unparseable; null yields inversion "other" rather than a wrong guess.
|
|
35
|
+
*/
|
|
36
|
+
export declare function positionFacts(pos: ChordsDbPosition, openMidi: number[], rootPc: number | null): PositionFacts;
|
|
37
|
+
/**
|
|
38
|
+
* For each position, the index of the first earlier position that sounds
|
|
39
|
+
* exactly the same pitches, or null when it is the first of its kind.
|
|
40
|
+
*
|
|
41
|
+
* chords-db ships 63 such redundant positions across 62 guitar entries
|
|
42
|
+
* (ukulele has none). Callers offering alternate voicings must skip these or
|
|
43
|
+
* they will present visual twins.
|
|
44
|
+
*
|
|
45
|
+
* Flags rather than removes: frames' /api/frame exposes a positionIndex
|
|
46
|
+
* parameter, so renumbering positions would silently change existing results.
|
|
47
|
+
*/
|
|
48
|
+
export declare function duplicateVoicingMap(positions: ChordsDbPosition[], openMidi: number[]): Array<number | null>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { positionToMidi } from "./pitch.js";
|
|
2
|
+
/**
|
|
3
|
+
* Interval from root to bass → inversion name.
|
|
4
|
+
*
|
|
5
|
+
* On guitar this constrains the bass note only: doubling, note count and
|
|
6
|
+
* register are all unconstrained, so "root position" means far less here than
|
|
7
|
+
* on a keyboard. Both major and minor thirds map to 1st inversion, and altered
|
|
8
|
+
* fifths to 2nd, so dim/aug shapes classify rather than falling through.
|
|
9
|
+
*/
|
|
10
|
+
function inversionFor(interval) {
|
|
11
|
+
if (interval === 0)
|
|
12
|
+
return "root";
|
|
13
|
+
if (interval === 3 || interval === 4)
|
|
14
|
+
return "1st";
|
|
15
|
+
if (interval === 6 || interval === 7 || interval === 8)
|
|
16
|
+
return "2nd";
|
|
17
|
+
if (interval === 9 || interval === 10 || interval === 11)
|
|
18
|
+
return "3rd";
|
|
19
|
+
return "other";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Derive facts for one position.
|
|
23
|
+
*
|
|
24
|
+
* @param pos - chords-db position
|
|
25
|
+
* @param openMidi - open-string MIDI in chords-db index order
|
|
26
|
+
* @param rootPc - root pitch class from `rootPitchClass(label)`, or null when
|
|
27
|
+
* unparseable; null yields inversion "other" rather than a wrong guess.
|
|
28
|
+
*/
|
|
29
|
+
export function positionFacts(pos, openMidi, rootPc) {
|
|
30
|
+
const midi = positionToMidi(pos, openMidi);
|
|
31
|
+
const bassMidi = midi.length ? Math.min(...midi) : -1;
|
|
32
|
+
const bassPitchClass = bassMidi >= 0 ? bassMidi % 12 : -1;
|
|
33
|
+
const pitchClasses = [...new Set(midi.map((m) => m % 12))].sort((a, b) => a - b);
|
|
34
|
+
const hasBarre = pos.barres.length > 0;
|
|
35
|
+
const inversion = rootPc === null || bassPitchClass < 0
|
|
36
|
+
? "other"
|
|
37
|
+
: inversionFor((bassPitchClass - rootPc + 12) % 12);
|
|
38
|
+
return {
|
|
39
|
+
midi,
|
|
40
|
+
bassMidi,
|
|
41
|
+
bassPitchClass,
|
|
42
|
+
inversion,
|
|
43
|
+
noteCount: midi.length,
|
|
44
|
+
pitchClasses,
|
|
45
|
+
doubledCount: midi.length - pitchClasses.length,
|
|
46
|
+
hasBarre,
|
|
47
|
+
isOpenShape: !hasBarre && pos.baseFret === 1,
|
|
48
|
+
baseFret: pos.baseFret,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* For each position, the index of the first earlier position that sounds
|
|
53
|
+
* exactly the same pitches, or null when it is the first of its kind.
|
|
54
|
+
*
|
|
55
|
+
* chords-db ships 63 such redundant positions across 62 guitar entries
|
|
56
|
+
* (ukulele has none). Callers offering alternate voicings must skip these or
|
|
57
|
+
* they will present visual twins.
|
|
58
|
+
*
|
|
59
|
+
* Flags rather than removes: frames' /api/frame exposes a positionIndex
|
|
60
|
+
* parameter, so renumbering positions would silently change existing results.
|
|
61
|
+
*/
|
|
62
|
+
export function duplicateVoicingMap(positions, openMidi) {
|
|
63
|
+
const firstSeen = new Map();
|
|
64
|
+
return positions.map((pos, i) => {
|
|
65
|
+
const key = positionToMidi(pos, openMidi).join(",");
|
|
66
|
+
const seen = firstSeen.get(key);
|
|
67
|
+
if (seen === undefined) {
|
|
68
|
+
firstSeen.set(key, i);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
return seen;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=voicingFacts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voicingFacts.js","sourceRoot":"","sources":["../src/voicingFacts.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAwB5C;;;;;;;GAOG;AACH,SAAS,YAAY,CAAC,QAAgB;IACpC,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,QAAQ,KAAK,CAAC,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,IAAI,QAAQ,KAAK,CAAC,IAAI,QAAQ,KAAK,CAAC,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrE,IAAI,QAAQ,KAAK,CAAC,IAAI,QAAQ,KAAK,EAAE,IAAI,QAAQ,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACvE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAqB,EACrB,QAAkB,EAClB,MAAqB;IAErB,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,YAAY,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACjF,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAEvC,MAAM,SAAS,GACb,MAAM,KAAK,IAAI,IAAI,cAAc,GAAG,CAAC;QACnC,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,YAAY,CAAC,CAAC,cAAc,GAAG,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IAExD,OAAO;QACL,IAAI;QACJ,QAAQ;QACR,cAAc;QACd,SAAS;QACT,SAAS,EAAE,IAAI,CAAC,MAAM;QACtB,YAAY;QACZ,YAAY,EAAE,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC,MAAM;QAC/C,QAAQ;QACR,WAAW,EAAE,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,CAAC;QAC5C,QAAQ,EAAE,GAAG,CAAC,QAAQ;KACvB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CACjC,SAA6B,EAC7B,QAAkB;IAElB,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Choosing among a chord's stored shapes.
|
|
3
|
+
*
|
|
4
|
+
* The package offers a sensible default and a ranked list; which shape a
|
|
5
|
+
* product actually displays stays the consumer's decision.
|
|
6
|
+
*/
|
|
7
|
+
import type { Chord } from "svguitar";
|
|
8
|
+
import type { ChordsDbPosition, InstrumentId } from "./instruments.js";
|
|
9
|
+
import type { PositionFacts } from "./voicingFacts.js";
|
|
10
|
+
/**
|
|
11
|
+
* Difficulty rungs expressible as a filter over stored shapes.
|
|
12
|
+
*
|
|
13
|
+
* The `guitar-top3` and `power` rungs are not filters — top-3 comes from
|
|
14
|
+
* GUITAR_TOP3_PRESETS and power chords are generated (src/powerChords.ts),
|
|
15
|
+
* because chords-db has neither.
|
|
16
|
+
*/
|
|
17
|
+
export type ShapeClass = "open" | "no-barre" | "any";
|
|
18
|
+
/** Rungs ordered easiest → hardest, for the "widen by one rung" option. */
|
|
19
|
+
export declare const SHAPE_CLASS_LADDER: ShapeClass[];
|
|
20
|
+
export declare function matchesShapeClass(facts: PositionFacts, cls: ShapeClass): boolean;
|
|
21
|
+
export interface CanonicalOptions {
|
|
22
|
+
instrument: InstrumentId;
|
|
23
|
+
/** Root pitch class from `rootPitchClass(label)`; null disables inversion ranking. */
|
|
24
|
+
rootPc: number | null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Index of the shape to present by default.
|
|
28
|
+
*
|
|
29
|
+
* Guitar: the first root-position shape, falling back to the first shape when
|
|
30
|
+
* none is root position (50 of 529 guitar entries have none).
|
|
31
|
+
*
|
|
32
|
+
* Ukulele: lowest baseFret, because root position barely applies — its
|
|
33
|
+
* reentrant high-G tuning leaves 317 of 552 entries with no root-position
|
|
34
|
+
* shape at all.
|
|
35
|
+
*
|
|
36
|
+
* Duplicate voicings are never chosen.
|
|
37
|
+
*/
|
|
38
|
+
export declare function canonicalPositionIndex(positions: ChordsDbPosition[], opts: CanonicalOptions): number;
|
|
39
|
+
export interface VoicingChoice {
|
|
40
|
+
/** Index into the original positions array — stable, so it round-trips. */
|
|
41
|
+
index: number;
|
|
42
|
+
facts: PositionFacts;
|
|
43
|
+
shape: Chord;
|
|
44
|
+
/**
|
|
45
|
+
* Human label derived from facts, e.g. "Open", "Barre, fret 8". English
|
|
46
|
+
* display text only — consumers who need their own wording or i18n should
|
|
47
|
+
* derive it from `facts` instead, which carries every fact this label is
|
|
48
|
+
* built from.
|
|
49
|
+
*/
|
|
50
|
+
label: string;
|
|
51
|
+
}
|
|
52
|
+
export interface SelectVoicingsOptions extends CanonicalOptions {
|
|
53
|
+
/** How many alternates to return alongside the primary. */
|
|
54
|
+
alternates: number;
|
|
55
|
+
/** Restrict candidates to a difficulty rung. Default "any". */
|
|
56
|
+
shapeClass?: ShapeClass;
|
|
57
|
+
/** Widen the pool by one rung when the requested rung runs dry. Default false. */
|
|
58
|
+
allowNextRung?: boolean;
|
|
59
|
+
title?: string;
|
|
60
|
+
}
|
|
61
|
+
export interface SelectVoicingsResult {
|
|
62
|
+
primary: VoicingChoice;
|
|
63
|
+
alternates: VoicingChoice[];
|
|
64
|
+
/** True when fewer alternates were available than requested. */
|
|
65
|
+
short: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A primary shape plus up to `alternates` maximally-contrasting others.
|
|
69
|
+
*
|
|
70
|
+
* Alternates are chosen greedily: each pick maximises its minimum contrast
|
|
71
|
+
* against everything already chosen. Naive first-n selection repeats an
|
|
72
|
+
* (inversion, barre) profile on 74.9% of guitar entries, which reads as a
|
|
73
|
+
* broken card when three shapes appear side by side.
|
|
74
|
+
*
|
|
75
|
+
* Duplicate voicings are excluded, so a card can never print visual twins.
|
|
76
|
+
* Returns fewer alternates than asked for rather than padding, with `short`
|
|
77
|
+
* set — 5 guitar entries have fewer than 3 unique voicings.
|
|
78
|
+
*
|
|
79
|
+
* Returns `null` in two distinct cases: an empty `positions` array, or a
|
|
80
|
+
* `positions` array whose entries all fail the requested `shapeClass` (after
|
|
81
|
+
* `allowNextRung` widening, if set). The second case is common, not
|
|
82
|
+
* exceptional — `shapeClass: "open"` alone returns `null` for roughly 40% of
|
|
83
|
+
* entries (220/529 guitar, 211/552 ukulele), so callers must handle `null`
|
|
84
|
+
* as a normal outcome rather than an error.
|
|
85
|
+
*
|
|
86
|
+
* When the requested `shapeClass` filters out the corpus-wide canonical
|
|
87
|
+
* position entirely, the primary falls back to `primaryPool[0]` (array
|
|
88
|
+
* order) rather than re-running root-position preference within the
|
|
89
|
+
* filtered pool. Measured at ~0.9% of entries — accepted as a known
|
|
90
|
+
* shortcut, not an oversight.
|
|
91
|
+
*/
|
|
92
|
+
export declare function selectVoicings(positions: ChordsDbPosition[], opts: SelectVoicingsOptions): SelectVoicingsResult | null;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { INSTRUMENTS, dbPositionToChord } from "./instruments.js";
|
|
2
|
+
import { positionFacts, duplicateVoicingMap } from "./voicingFacts.js";
|
|
3
|
+
/** Rungs ordered easiest → hardest, for the "widen by one rung" option. */
|
|
4
|
+
export const SHAPE_CLASS_LADDER = ["open", "no-barre", "any"];
|
|
5
|
+
export function matchesShapeClass(facts, cls) {
|
|
6
|
+
if (cls === "any")
|
|
7
|
+
return true;
|
|
8
|
+
if (cls === "no-barre")
|
|
9
|
+
return !facts.hasBarre;
|
|
10
|
+
return facts.isOpenShape;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Index of the shape to present by default.
|
|
14
|
+
*
|
|
15
|
+
* Guitar: the first root-position shape, falling back to the first shape when
|
|
16
|
+
* none is root position (50 of 529 guitar entries have none).
|
|
17
|
+
*
|
|
18
|
+
* Ukulele: lowest baseFret, because root position barely applies — its
|
|
19
|
+
* reentrant high-G tuning leaves 317 of 552 entries with no root-position
|
|
20
|
+
* shape at all.
|
|
21
|
+
*
|
|
22
|
+
* Duplicate voicings are never chosen.
|
|
23
|
+
*/
|
|
24
|
+
export function canonicalPositionIndex(positions, opts) {
|
|
25
|
+
if (positions.length === 0)
|
|
26
|
+
return -1;
|
|
27
|
+
const openMidi = INSTRUMENTS[opts.instrument].openMidi;
|
|
28
|
+
const dupes = duplicateVoicingMap(positions, openMidi);
|
|
29
|
+
const candidates = positions
|
|
30
|
+
.map((pos, index) => ({ index, facts: positionFacts(pos, openMidi, opts.rootPc) }))
|
|
31
|
+
.filter((c) => dupes[c.index] === null);
|
|
32
|
+
if (opts.instrument === "ukulele") {
|
|
33
|
+
let best = candidates[0];
|
|
34
|
+
for (const c of candidates) {
|
|
35
|
+
if (c.facts.baseFret < best.facts.baseFret)
|
|
36
|
+
best = c;
|
|
37
|
+
}
|
|
38
|
+
return best.index;
|
|
39
|
+
}
|
|
40
|
+
const rootPosition = candidates.find((c) => c.facts.inversion === "root");
|
|
41
|
+
return (rootPosition ?? candidates[0]).index;
|
|
42
|
+
}
|
|
43
|
+
function labelFor(facts) {
|
|
44
|
+
if (facts.isOpenShape)
|
|
45
|
+
return "Open";
|
|
46
|
+
if (facts.hasBarre)
|
|
47
|
+
return `Barre, fret ${facts.baseFret}`;
|
|
48
|
+
return `Fret ${facts.baseFret}`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* How different two shapes look and feel to a player. Higher = more contrast.
|
|
52
|
+
*
|
|
53
|
+
* Inversion dominates because it changes what the chord sounds like; barre vs
|
|
54
|
+
* open is the next most visible difference; neck distance breaks ties.
|
|
55
|
+
*/
|
|
56
|
+
function contrast(a, b) {
|
|
57
|
+
let score = 0;
|
|
58
|
+
if (a.inversion !== b.inversion)
|
|
59
|
+
score += 4;
|
|
60
|
+
if (a.hasBarre !== b.hasBarre)
|
|
61
|
+
score += 2;
|
|
62
|
+
score += Math.min(Math.abs(a.baseFret - b.baseFret), 5) / 5;
|
|
63
|
+
return score;
|
|
64
|
+
}
|
|
65
|
+
function widen(cls) {
|
|
66
|
+
const i = SHAPE_CLASS_LADDER.indexOf(cls);
|
|
67
|
+
return SHAPE_CLASS_LADDER[Math.min(i + 1, SHAPE_CLASS_LADDER.length - 1)];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A primary shape plus up to `alternates` maximally-contrasting others.
|
|
71
|
+
*
|
|
72
|
+
* Alternates are chosen greedily: each pick maximises its minimum contrast
|
|
73
|
+
* against everything already chosen. Naive first-n selection repeats an
|
|
74
|
+
* (inversion, barre) profile on 74.9% of guitar entries, which reads as a
|
|
75
|
+
* broken card when three shapes appear side by side.
|
|
76
|
+
*
|
|
77
|
+
* Duplicate voicings are excluded, so a card can never print visual twins.
|
|
78
|
+
* Returns fewer alternates than asked for rather than padding, with `short`
|
|
79
|
+
* set — 5 guitar entries have fewer than 3 unique voicings.
|
|
80
|
+
*
|
|
81
|
+
* Returns `null` in two distinct cases: an empty `positions` array, or a
|
|
82
|
+
* `positions` array whose entries all fail the requested `shapeClass` (after
|
|
83
|
+
* `allowNextRung` widening, if set). The second case is common, not
|
|
84
|
+
* exceptional — `shapeClass: "open"` alone returns `null` for roughly 40% of
|
|
85
|
+
* entries (220/529 guitar, 211/552 ukulele), so callers must handle `null`
|
|
86
|
+
* as a normal outcome rather than an error.
|
|
87
|
+
*
|
|
88
|
+
* When the requested `shapeClass` filters out the corpus-wide canonical
|
|
89
|
+
* position entirely, the primary falls back to `primaryPool[0]` (array
|
|
90
|
+
* order) rather than re-running root-position preference within the
|
|
91
|
+
* filtered pool. Measured at ~0.9% of entries — accepted as a known
|
|
92
|
+
* shortcut, not an oversight.
|
|
93
|
+
*/
|
|
94
|
+
export function selectVoicings(positions, opts) {
|
|
95
|
+
if (positions.length === 0)
|
|
96
|
+
return null;
|
|
97
|
+
const openMidi = INSTRUMENTS[opts.instrument].openMidi;
|
|
98
|
+
const dupes = duplicateVoicingMap(positions, openMidi);
|
|
99
|
+
const stringCount = INSTRUMENTS[opts.instrument].strings;
|
|
100
|
+
const build = (index) => {
|
|
101
|
+
const facts = positionFacts(positions[index], openMidi, opts.rootPc);
|
|
102
|
+
return {
|
|
103
|
+
index,
|
|
104
|
+
facts,
|
|
105
|
+
shape: dbPositionToChord(positions[index], stringCount, opts.title),
|
|
106
|
+
label: labelFor(facts),
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
const requested = opts.shapeClass ?? "any";
|
|
110
|
+
const classes = opts.allowNextRung ? [requested, widen(requested)] : [requested];
|
|
111
|
+
// narrowPool is the pool for the originally requested rung; pool may widen
|
|
112
|
+
// beyond it when allowNextRung kicks in. The primary must come from
|
|
113
|
+
// narrowPool whenever it has candidates — widening exists to backfill
|
|
114
|
+
// alternates, not to bump the primary to a harder rung. See Fix 4.
|
|
115
|
+
let pool = [];
|
|
116
|
+
let narrowPool = [];
|
|
117
|
+
for (const cls of classes) {
|
|
118
|
+
pool = positions
|
|
119
|
+
.map((_, index) => index)
|
|
120
|
+
.filter((index) => dupes[index] === null)
|
|
121
|
+
.map(build)
|
|
122
|
+
.filter((c) => matchesShapeClass(c.facts, cls));
|
|
123
|
+
if (cls === requested)
|
|
124
|
+
narrowPool = pool;
|
|
125
|
+
if (pool.length > opts.alternates)
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
if (pool.length === 0)
|
|
129
|
+
return null;
|
|
130
|
+
const canonical = canonicalPositionIndex(positions, opts);
|
|
131
|
+
const primaryPool = narrowPool.length > 0 ? narrowPool : pool;
|
|
132
|
+
// Fallback to primaryPool[0] documented on the function's JSDoc above.
|
|
133
|
+
const primary = primaryPool.find((c) => c.index === canonical) ?? primaryPool[0];
|
|
134
|
+
const chosen = [primary];
|
|
135
|
+
const remaining = pool.filter((c) => c.index !== primary.index);
|
|
136
|
+
while (chosen.length <= opts.alternates && remaining.length > 0) {
|
|
137
|
+
let bestAt = 0;
|
|
138
|
+
let bestScore = -Infinity;
|
|
139
|
+
remaining.forEach((cand, i) => {
|
|
140
|
+
const score = Math.min(...chosen.map((c) => contrast(c.facts, cand.facts)));
|
|
141
|
+
if (score > bestScore) {
|
|
142
|
+
bestScore = score;
|
|
143
|
+
bestAt = i;
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
chosen.push(remaining.splice(bestAt, 1)[0]);
|
|
147
|
+
}
|
|
148
|
+
const alternates = chosen.slice(1);
|
|
149
|
+
return { primary, alternates, short: alternates.length < opts.alternates };
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=voicingSelect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voicingSelect.js","sourceRoot":"","sources":["../src/voicingSelect.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pepperhorn/chordl-guitar",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Guitar/ukulele chord-shape lookup and svguitar diagram data — chords-db backed, with alternate fret positions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=20.10"
|
|
18
|
+
},
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@tombatossals/chords-db": "^0.5.1",
|
|
25
|
+
"svguitar": "^2.5.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"typescript": "^5.7.0",
|
|
29
|
+
"vitest": "^4.1.1"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build:data": "node scripts/build-data.mjs",
|
|
37
|
+
"build": "pnpm run build:data && tsc",
|
|
38
|
+
"lint": "pnpm run build:data && tsc --noEmit",
|
|
39
|
+
"test": "pnpm run build:data && vitest",
|
|
40
|
+
"test:run": "pnpm run build:data && vitest run"
|
|
41
|
+
}
|
|
42
|
+
}
|