@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,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated (not stored) shapes: bass root-fifth-octave patterns and violin
|
|
3
|
+
* first-position placements.
|
|
4
|
+
*
|
|
5
|
+
* chords-db covers guitar and ukulele only, and neither instrument here wants
|
|
6
|
+
* a database anyway:
|
|
7
|
+
*
|
|
8
|
+
* - A bass line is built from patterns, not strummed chords. The teachable
|
|
9
|
+
* unit is the root-fifth-octave shape, which is the same movable pattern
|
|
10
|
+
* for every root — so it is computed from the root, and the computation IS
|
|
11
|
+
* the lesson.
|
|
12
|
+
* - Violin chord tones in first position are fully determined by the tuning
|
|
13
|
+
* and the first-position reach (open + four fingers ≈ 0-7 semitones above
|
|
14
|
+
* the open string). Given a chord's pitch classes, each string's nearest
|
|
15
|
+
* chord tone is arithmetic, not curation.
|
|
16
|
+
*
|
|
17
|
+
* Both return svguitar `Chord` objects so they render through exactly the
|
|
18
|
+
* same pipeline as the chords-db shapes.
|
|
19
|
+
*/
|
|
20
|
+
import type { Chord } from "svguitar";
|
|
21
|
+
export declare function bassShapeFor(root: string): Chord | null;
|
|
22
|
+
/**
|
|
23
|
+
* Violin first-position shape for a chord's pitch classes.
|
|
24
|
+
*
|
|
25
|
+
* The placement is chosen across all four strings at once, not string by
|
|
26
|
+
* string. Picking each string's *nearest* chord tone independently is the
|
|
27
|
+
* obvious approach and it is wrong: on A major the G string's nearest tone is
|
|
28
|
+
* A, the D string's is E, and both open strings are already A and E, so the
|
|
29
|
+
* card sounds A-E-A-E and the third never appears. That greedy version
|
|
30
|
+
* silently dropped a chord tone in 32 of the 60 chords this library generates,
|
|
31
|
+
* including the root of C7, B7 and F#7. A chord card that omits the third is
|
|
32
|
+
* not teaching the chord.
|
|
33
|
+
*
|
|
34
|
+
* So: enumerate every assignment (each string plays one reachable chord tone
|
|
35
|
+
* or is silent) and rank them by
|
|
36
|
+
* 1. how many DISTINCT chord tones sound — the thing that was broken;
|
|
37
|
+
* 2. how many strings sound at all — prefer a fuller chord;
|
|
38
|
+
* 3. total semitone offset — prefer the lower, easier hand shape;
|
|
39
|
+
* 4. the offsets read low-string-first, purely so the result is stable.
|
|
40
|
+
* Four strings with at most five options each is 625 combinations at worst,
|
|
41
|
+
* so the exhaustive search costs nothing and removes the whole class of bug.
|
|
42
|
+
*
|
|
43
|
+
* A tone can still be unreachable — with only 0-7 semitones above four fixed
|
|
44
|
+
* open strings, not every chord fits — so criterion 1 maximises coverage
|
|
45
|
+
* rather than guaranteeing it. `violinCoverage` reports what was achieved.
|
|
46
|
+
*
|
|
47
|
+
* The diagram's "fret" axis is semitones above the open string; markers are
|
|
48
|
+
* labelled with VIOLIN finger numbers via `violinFingerFor`, not guitar
|
|
49
|
+
* fingerings.
|
|
50
|
+
*/
|
|
51
|
+
export declare function violinShapeFor(pitchClasses: string[]): Chord | null;
|
|
52
|
+
/**
|
|
53
|
+
* Which of a chord's pitch classes the first-position shape actually sounds.
|
|
54
|
+
*
|
|
55
|
+
* Exists so callers and tests can assert coverage instead of trusting it —
|
|
56
|
+
* the bug this replaced was invisible precisely because nothing measured it.
|
|
57
|
+
*/
|
|
58
|
+
export declare function violinCoverage(pitchClasses: string[]): {
|
|
59
|
+
sounded: number[];
|
|
60
|
+
missing: number[];
|
|
61
|
+
} | null;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated (not stored) shapes: bass root-fifth-octave patterns and violin
|
|
3
|
+
* first-position placements.
|
|
4
|
+
*
|
|
5
|
+
* chords-db covers guitar and ukulele only, and neither instrument here wants
|
|
6
|
+
* a database anyway:
|
|
7
|
+
*
|
|
8
|
+
* - A bass line is built from patterns, not strummed chords. The teachable
|
|
9
|
+
* unit is the root-fifth-octave shape, which is the same movable pattern
|
|
10
|
+
* for every root — so it is computed from the root, and the computation IS
|
|
11
|
+
* the lesson.
|
|
12
|
+
* - Violin chord tones in first position are fully determined by the tuning
|
|
13
|
+
* and the first-position reach (open + four fingers ≈ 0-7 semitones above
|
|
14
|
+
* the open string). Given a chord's pitch classes, each string's nearest
|
|
15
|
+
* chord tone is arithmetic, not curation.
|
|
16
|
+
*
|
|
17
|
+
* Both return svguitar `Chord` objects so they render through exactly the
|
|
18
|
+
* same pipeline as the chords-db shapes.
|
|
19
|
+
*/
|
|
20
|
+
const PC = {
|
|
21
|
+
C: 0, "C#": 1, Db: 1, D: 2, "D#": 3, Eb: 3, E: 4, F: 5, "F#": 6, Gb: 6,
|
|
22
|
+
G: 7, "G#": 8, Ab: 8, A: 9, "A#": 10, Bb: 10, B: 11,
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Bass root-fifth-octave shape for one root, placed where it sounds good.
|
|
26
|
+
*
|
|
27
|
+
* The root always lands FRETTED between the 3rd and 9th fret — never on an
|
|
28
|
+
* open string and never crammed against the nut. Two reasons, both audible:
|
|
29
|
+
* open-position roots boom and mud on a bass, and the whole point of the
|
|
30
|
+
* pattern is that it is movable, which an open string breaks (you cannot
|
|
31
|
+
* slide an open E up a tone). E string is preferred when its root fret sits
|
|
32
|
+
* in the sweet range; otherwise the A string's does (every root lands in
|
|
33
|
+
* 3-9 on one of the two).
|
|
34
|
+
*
|
|
35
|
+
* The window slides to the root (chords-db convention, matching
|
|
36
|
+
* `dbPositionToChord`: `position` is the window's first fret, finger frets
|
|
37
|
+
* window-relative) — D at the 5th fret is the same shape as G at the 3rd.
|
|
38
|
+
*
|
|
39
|
+
* svguitar strings are numbered from the highest pitch: 1=G, 2=D, 3=A, 4=E.
|
|
40
|
+
* Fingers carry the conventional one-finger-per-fret labels: root 1,
|
|
41
|
+
* fifth 3, octave 4.
|
|
42
|
+
*/
|
|
43
|
+
const BASS_SWEET_LOW = 3;
|
|
44
|
+
const BASS_SWEET_HIGH = 9;
|
|
45
|
+
export function bassShapeFor(root) {
|
|
46
|
+
const pc = PC[root];
|
|
47
|
+
if (pc === undefined)
|
|
48
|
+
return null;
|
|
49
|
+
const fretOnE = (pc - PC.E + 12) % 12;
|
|
50
|
+
const fretOnA = (pc - PC.A + 12) % 12;
|
|
51
|
+
const sweet = (f) => f >= BASS_SWEET_LOW && f <= BASS_SWEET_HIGH;
|
|
52
|
+
const onEString = sweet(fretOnE) || !sweet(fretOnA);
|
|
53
|
+
const rootFret = onEString ? fretOnE : fretOnA;
|
|
54
|
+
// [rootString, fifthString, octaveString] in svguitar numbering.
|
|
55
|
+
const [rootS, fifthS, octaveS] = onEString ? [4, 3, 2] : [3, 2, 1];
|
|
56
|
+
const mutedS = onEString ? 1 : 4;
|
|
57
|
+
// Slide the window to the root whenever the pattern outgrows the nut view.
|
|
58
|
+
const slide = rootFret + 2 > 6;
|
|
59
|
+
const position = slide ? rootFret : 1;
|
|
60
|
+
const rel = (abs) => (slide ? abs - rootFret + 1 : abs);
|
|
61
|
+
return {
|
|
62
|
+
fingers: [
|
|
63
|
+
[rootS, rel(rootFret), "1"],
|
|
64
|
+
[fifthS, rel(rootFret + 2), "3"],
|
|
65
|
+
[octaveS, rel(rootFret + 2), "4"],
|
|
66
|
+
[mutedS, "x"],
|
|
67
|
+
],
|
|
68
|
+
barres: [],
|
|
69
|
+
position,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* First-position reach: how far above the open string each violin finger
|
|
74
|
+
* falls, in semitones. Low/high placements share a finger — offset 1 or 2 is
|
|
75
|
+
* first finger, 3 or 4 second, and so on — which is exactly how first
|
|
76
|
+
* position is named on a fingering chart.
|
|
77
|
+
*/
|
|
78
|
+
function violinFingerFor(offset) {
|
|
79
|
+
if (offset === 0)
|
|
80
|
+
return null; // open string: nut ring, no label
|
|
81
|
+
if (offset <= 2)
|
|
82
|
+
return "1";
|
|
83
|
+
if (offset <= 4)
|
|
84
|
+
return "2";
|
|
85
|
+
if (offset <= 6)
|
|
86
|
+
return "3";
|
|
87
|
+
return "4"; // offset 7 — fourth finger, the octave of the next string down
|
|
88
|
+
}
|
|
89
|
+
const FIRST_POSITION_REACH = 7;
|
|
90
|
+
/**
|
|
91
|
+
* Violin first-position shape for a chord's pitch classes.
|
|
92
|
+
*
|
|
93
|
+
* The placement is chosen across all four strings at once, not string by
|
|
94
|
+
* string. Picking each string's *nearest* chord tone independently is the
|
|
95
|
+
* obvious approach and it is wrong: on A major the G string's nearest tone is
|
|
96
|
+
* A, the D string's is E, and both open strings are already A and E, so the
|
|
97
|
+
* card sounds A-E-A-E and the third never appears. That greedy version
|
|
98
|
+
* silently dropped a chord tone in 32 of the 60 chords this library generates,
|
|
99
|
+
* including the root of C7, B7 and F#7. A chord card that omits the third is
|
|
100
|
+
* not teaching the chord.
|
|
101
|
+
*
|
|
102
|
+
* So: enumerate every assignment (each string plays one reachable chord tone
|
|
103
|
+
* or is silent) and rank them by
|
|
104
|
+
* 1. how many DISTINCT chord tones sound — the thing that was broken;
|
|
105
|
+
* 2. how many strings sound at all — prefer a fuller chord;
|
|
106
|
+
* 3. total semitone offset — prefer the lower, easier hand shape;
|
|
107
|
+
* 4. the offsets read low-string-first, purely so the result is stable.
|
|
108
|
+
* Four strings with at most five options each is 625 combinations at worst,
|
|
109
|
+
* so the exhaustive search costs nothing and removes the whole class of bug.
|
|
110
|
+
*
|
|
111
|
+
* A tone can still be unreachable — with only 0-7 semitones above four fixed
|
|
112
|
+
* open strings, not every chord fits — so criterion 1 maximises coverage
|
|
113
|
+
* rather than guaranteeing it. `violinCoverage` reports what was achieved.
|
|
114
|
+
*
|
|
115
|
+
* The diagram's "fret" axis is semitones above the open string; markers are
|
|
116
|
+
* labelled with VIOLIN finger numbers via `violinFingerFor`, not guitar
|
|
117
|
+
* fingerings.
|
|
118
|
+
*/
|
|
119
|
+
export function violinShapeFor(pitchClasses) {
|
|
120
|
+
const tones = pitchClasses.map((p) => PC[p]);
|
|
121
|
+
if (tones.some((t) => t === undefined) || tones.length === 0)
|
|
122
|
+
return null;
|
|
123
|
+
const wanted = [...new Set(tones)];
|
|
124
|
+
// Low to high: G D A E; svguitar numbering: 4=G, 3=D, 2=A, 1=E.
|
|
125
|
+
const strings = [
|
|
126
|
+
{ svguitarString: 4, openPc: PC.G },
|
|
127
|
+
{ svguitarString: 3, openPc: PC.D },
|
|
128
|
+
{ svguitarString: 2, openPc: PC.A },
|
|
129
|
+
{ svguitarString: 1, openPc: PC.E },
|
|
130
|
+
];
|
|
131
|
+
// Per string: every reachable chord tone, plus silence. Within a 0-7 window
|
|
132
|
+
// a pitch class occurs at most once, so one offset per tone per string.
|
|
133
|
+
const choices = strings.map(({ openPc }) => {
|
|
134
|
+
const opts = [];
|
|
135
|
+
for (const tone of wanted) {
|
|
136
|
+
const offset = (tone - openPc + 12) % 12;
|
|
137
|
+
if (offset <= FIRST_POSITION_REACH)
|
|
138
|
+
opts.push({ offset, tone });
|
|
139
|
+
}
|
|
140
|
+
opts.push(null); // silent — better than inventing a placement out of position
|
|
141
|
+
return opts;
|
|
142
|
+
});
|
|
143
|
+
// Cartesian product of the per-string options. Four strings, at most five
|
|
144
|
+
// options each — 625 combinations at the absolute worst.
|
|
145
|
+
let combos = [[]];
|
|
146
|
+
for (const opts of choices) {
|
|
147
|
+
const next = [];
|
|
148
|
+
for (const combo of combos)
|
|
149
|
+
for (const opt of opts)
|
|
150
|
+
next.push([...combo, opt]);
|
|
151
|
+
combos = next;
|
|
152
|
+
}
|
|
153
|
+
let best = null;
|
|
154
|
+
for (const picks of combos) {
|
|
155
|
+
const sounded = picks.filter((p) => p !== null);
|
|
156
|
+
const covered = new Set(sounded.map((p) => p.tone)).size;
|
|
157
|
+
const total = sounded.reduce((n, p) => n + p.offset, 0);
|
|
158
|
+
// Negated where higher is better, so the whole score compares smallest-first.
|
|
159
|
+
const score = [
|
|
160
|
+
-covered,
|
|
161
|
+
-sounded.length,
|
|
162
|
+
total,
|
|
163
|
+
...picks.map((p) => (p === null ? 99 : p.offset)),
|
|
164
|
+
];
|
|
165
|
+
if (best === null || lessThan(score, best.score))
|
|
166
|
+
best = { picks, score };
|
|
167
|
+
}
|
|
168
|
+
if (best === null)
|
|
169
|
+
return null;
|
|
170
|
+
const fingers = best.picks.map((pick, i) => {
|
|
171
|
+
const s = strings[i].svguitarString;
|
|
172
|
+
if (pick === null)
|
|
173
|
+
return [s, "x"];
|
|
174
|
+
const label = violinFingerFor(pick.offset);
|
|
175
|
+
return label ? [s, pick.offset, label] : [s, pick.offset];
|
|
176
|
+
});
|
|
177
|
+
return { fingers, barres: [] };
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Which of a chord's pitch classes the first-position shape actually sounds.
|
|
181
|
+
*
|
|
182
|
+
* Exists so callers and tests can assert coverage instead of trusting it —
|
|
183
|
+
* the bug this replaced was invisible precisely because nothing measured it.
|
|
184
|
+
*/
|
|
185
|
+
export function violinCoverage(pitchClasses) {
|
|
186
|
+
const shape = violinShapeFor(pitchClasses);
|
|
187
|
+
if (shape === null)
|
|
188
|
+
return null;
|
|
189
|
+
const wanted = [...new Set(pitchClasses.map((p) => PC[p]))];
|
|
190
|
+
const openPcs = [PC.E, PC.A, PC.D, PC.G]; // svguitar string 1..4
|
|
191
|
+
const sounded = new Set();
|
|
192
|
+
for (const f of shape.fingers) {
|
|
193
|
+
if (f[1] === "x")
|
|
194
|
+
continue;
|
|
195
|
+
sounded.add((openPcs[Number(f[0]) - 1] + Number(f[1])) % 12);
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
sounded: [...sounded].sort((a, b) => a - b),
|
|
199
|
+
missing: wanted.filter((t) => !sounded.has(t)).sort((a, b) => a - b),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/** Lexicographic compare for the ranking tuple above. */
|
|
203
|
+
function lessThan(a, b) {
|
|
204
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
205
|
+
const x = a[i] ?? 0;
|
|
206
|
+
const y = b[i] ?? 0;
|
|
207
|
+
if (x !== y)
|
|
208
|
+
return x < y;
|
|
209
|
+
}
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=generatedShapes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generatedShapes.js","sourceRoot":"","sources":["../src/generatedShapes.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { INSTRUMENTS, dbPositionToChord } from "./instruments.js";
|
|
2
|
+
export type { InstrumentId, InstrumentConfig, ChordsDb, ChordsDbEntry, ChordsDbPosition, } from "./instruments.js";
|
|
3
|
+
export { lookupGuitarChord, hasGuitarChord } from "./chordLookup.js";
|
|
4
|
+
export type { GuitarChordResult } from "./chordLookup.js";
|
|
5
|
+
export { GUITAR_TOP3_PRESETS, lookupTop3Chord } from "./staticPresets.js";
|
|
6
|
+
export type { StaticPreset } from "./staticPresets.js";
|
|
7
|
+
export { positionToMidi, rootPitchClass } from "./pitch.js";
|
|
8
|
+
export { positionFacts, duplicateVoicingMap } from "./voicingFacts.js";
|
|
9
|
+
export type { Inversion, PositionFacts } from "./voicingFacts.js";
|
|
10
|
+
export { matchesShapeClass, canonicalPositionIndex, selectVoicings, SHAPE_CLASS_LADDER, } from "./voicingSelect.js";
|
|
11
|
+
export type { ShapeClass, CanonicalOptions, VoicingChoice, SelectVoicingsOptions, SelectVoicingsResult, } from "./voicingSelect.js";
|
|
12
|
+
export { powerChordPosition, powerChordShape } from "./powerChords.js";
|
|
13
|
+
export type { PowerChordStringSet, PowerChordOptions } from "./powerChords.js";
|
|
14
|
+
export { bassShapeFor, violinShapeFor, violinCoverage } from "./generatedShapes.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// @pepperhorn/chordl-guitar — guitar chord-shape lookup and svguitar diagram data.
|
|
2
|
+
//
|
|
3
|
+
// Backed by @tombatossals/chords-db; returns every stored fret position for a
|
|
4
|
+
// chord (alternate placements). Shapes convert to svguitar `Chord` objects for
|
|
5
|
+
// rendering. Based on pepperhorn/frames.
|
|
6
|
+
export { INSTRUMENTS, dbPositionToChord } from "./instruments.js";
|
|
7
|
+
export { lookupGuitarChord, hasGuitarChord } from "./chordLookup.js";
|
|
8
|
+
export { GUITAR_TOP3_PRESETS, lookupTop3Chord } from "./staticPresets.js";
|
|
9
|
+
export { positionToMidi, rootPitchClass } from "./pitch.js";
|
|
10
|
+
export { positionFacts, duplicateVoicingMap } from "./voicingFacts.js";
|
|
11
|
+
export { matchesShapeClass, canonicalPositionIndex, selectVoicings, SHAPE_CLASS_LADDER, } from "./voicingSelect.js";
|
|
12
|
+
export { powerChordPosition, powerChordShape } from "./powerChords.js";
|
|
13
|
+
export { bassShapeFor, violinShapeFor, violinCoverage } from "./generatedShapes.js";
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,yCAAyC;AAEzC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AASlE,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGrE,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAG1E,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5D,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGvE,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAS5B,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGvE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instrument configs + chords-db types + position→svguitar conversion.
|
|
3
|
+
*
|
|
4
|
+
* Ported/adapted from pepperhorn/frames (src/lib/instruments.ts). chords-db
|
|
5
|
+
* ships guitar and ukulele voicing libraries; we start with guitar and keep the
|
|
6
|
+
* config table so ukulele is a small follow-up.
|
|
7
|
+
*/
|
|
8
|
+
import type { Chord } from "svguitar";
|
|
9
|
+
export type InstrumentId = "guitar" | "ukulele" | "guitar-top3" | "bass4" | "bass5" | "violin";
|
|
10
|
+
export interface InstrumentConfig {
|
|
11
|
+
id: InstrumentId;
|
|
12
|
+
label: string;
|
|
13
|
+
description: string;
|
|
14
|
+
strings: number;
|
|
15
|
+
/** Default number of frets to draw. */
|
|
16
|
+
frets: number;
|
|
17
|
+
/** Open-string tuning, low → high. */
|
|
18
|
+
tuning: string[];
|
|
19
|
+
/**
|
|
20
|
+
* Open-string MIDI numbers in **chords-db index order** — `openMidi[i]` is
|
|
21
|
+
* the string that `frets[i]` refers to. This is NOT svguitar's string
|
|
22
|
+
* numbering, which runs highest-pitch-first; `dbPositionToChord` is the one
|
|
23
|
+
* place that inverts. Ukulele's reentrant high-G means the array is not
|
|
24
|
+
* ascending, so the order cannot be recovered by sorting.
|
|
25
|
+
*/
|
|
26
|
+
openMidi: number[];
|
|
27
|
+
}
|
|
28
|
+
export declare const INSTRUMENTS: Record<InstrumentId, InstrumentConfig>;
|
|
29
|
+
export interface ChordsDbPosition {
|
|
30
|
+
/** Per-string fret, low→high. -1 = muted, 0 = open, else absolute-ish (see baseFret). */
|
|
31
|
+
frets: number[];
|
|
32
|
+
/** Per-string finger number (0 = none). */
|
|
33
|
+
fingers: number[];
|
|
34
|
+
/** Lowest fret the diagram window starts on (1 = nut). */
|
|
35
|
+
baseFret: number;
|
|
36
|
+
/** Fret numbers that carry a barre. */
|
|
37
|
+
barres: number[];
|
|
38
|
+
capo?: boolean;
|
|
39
|
+
midi?: number[];
|
|
40
|
+
}
|
|
41
|
+
export interface ChordsDbEntry {
|
|
42
|
+
key: string;
|
|
43
|
+
suffix: string;
|
|
44
|
+
positions: ChordsDbPosition[];
|
|
45
|
+
}
|
|
46
|
+
export interface ChordsDb {
|
|
47
|
+
main: {
|
|
48
|
+
strings: number;
|
|
49
|
+
fretsOnChord: number;
|
|
50
|
+
name: string;
|
|
51
|
+
numberOfChords: number;
|
|
52
|
+
};
|
|
53
|
+
tunings: Record<string, string[]>;
|
|
54
|
+
keys: string[];
|
|
55
|
+
suffixes: string[];
|
|
56
|
+
chords: Record<string, ChordsDbEntry[]>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Convert a chords-db position into an svguitar Chord (fingers + barres +
|
|
60
|
+
* window position). Ported from pepperhorn/frames.
|
|
61
|
+
*
|
|
62
|
+
* @param pos - chords-db position
|
|
63
|
+
* @param stringCount - number of strings (6 guitar, 4 ukulele)
|
|
64
|
+
* @param title - optional diagram title
|
|
65
|
+
*/
|
|
66
|
+
export declare function dbPositionToChord(pos: ChordsDbPosition, stringCount: number, title?: string): Chord;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export const INSTRUMENTS = {
|
|
2
|
+
guitar: {
|
|
3
|
+
id: "guitar",
|
|
4
|
+
label: "Guitar",
|
|
5
|
+
description: "Standard 6-string guitar (E A D G B E)",
|
|
6
|
+
strings: 6,
|
|
7
|
+
frets: 5,
|
|
8
|
+
tuning: ["E", "A", "D", "G", "B", "E"],
|
|
9
|
+
openMidi: [40, 45, 50, 55, 59, 64],
|
|
10
|
+
},
|
|
11
|
+
ukulele: {
|
|
12
|
+
id: "ukulele",
|
|
13
|
+
label: "Ukulele",
|
|
14
|
+
description: "Standard ukulele (G C E A)",
|
|
15
|
+
strings: 4,
|
|
16
|
+
frets: 5,
|
|
17
|
+
tuning: ["G", "C", "E", "A"],
|
|
18
|
+
openMidi: [67, 60, 64, 69],
|
|
19
|
+
},
|
|
20
|
+
"guitar-top3": {
|
|
21
|
+
id: "guitar-top3",
|
|
22
|
+
label: "Guitar (top 3)",
|
|
23
|
+
description: "Six-string guitar, beginner voicings on the top three strings only (G B E). " +
|
|
24
|
+
"Strings 4-6 are muted at render time.",
|
|
25
|
+
strings: 6,
|
|
26
|
+
frets: 4,
|
|
27
|
+
tuning: ["E", "A", "D", "G", "B", "E"],
|
|
28
|
+
openMidi: [40, 45, 50, 55, 59, 64],
|
|
29
|
+
},
|
|
30
|
+
bass4: {
|
|
31
|
+
id: "bass4",
|
|
32
|
+
label: "Bass (4-string)",
|
|
33
|
+
description: "Standard 4-string bass (E A D G). Also the instrument `bassShapeFor` " +
|
|
34
|
+
"targets: generated root-fifth-octave patterns rather than chords-db " +
|
|
35
|
+
"lookups, because a bass card teaches the pattern, not a strum.",
|
|
36
|
+
strings: 4,
|
|
37
|
+
// Six, not five: the generated root-fifth-octave patterns reach a sixth
|
|
38
|
+
// fret, and this count is what consumers draw the window from. Five clips
|
|
39
|
+
// them. See generatedShapes.bassShapeFor and its test.
|
|
40
|
+
frets: 6,
|
|
41
|
+
tuning: ["E", "A", "D", "G"],
|
|
42
|
+
openMidi: [28, 33, 38, 43],
|
|
43
|
+
},
|
|
44
|
+
bass5: {
|
|
45
|
+
id: "bass5",
|
|
46
|
+
label: "Bass (5-string)",
|
|
47
|
+
description: "5-string bass with low B (B E A D G)",
|
|
48
|
+
strings: 5,
|
|
49
|
+
frets: 5,
|
|
50
|
+
tuning: ["B", "E", "A", "D", "G"],
|
|
51
|
+
openMidi: [23, 28, 33, 38, 43],
|
|
52
|
+
},
|
|
53
|
+
violin: {
|
|
54
|
+
id: "violin",
|
|
55
|
+
label: "Violin",
|
|
56
|
+
description: "Violin in first position (G D A E). The \"fret\" axis is semitones above the " +
|
|
57
|
+
"open string (0-7, the first-position reach); markers carry violin finger " +
|
|
58
|
+
"numbers, not guitar fingerings. Generated from chord tones — see generatedShapes.",
|
|
59
|
+
strings: 4,
|
|
60
|
+
frets: 7,
|
|
61
|
+
tuning: ["G", "D", "A", "E"],
|
|
62
|
+
openMidi: [55, 62, 69, 76],
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Convert a chords-db position into an svguitar Chord (fingers + barres +
|
|
67
|
+
* window position). Ported from pepperhorn/frames.
|
|
68
|
+
*
|
|
69
|
+
* @param pos - chords-db position
|
|
70
|
+
* @param stringCount - number of strings (6 guitar, 4 ukulele)
|
|
71
|
+
* @param title - optional diagram title
|
|
72
|
+
*/
|
|
73
|
+
export function dbPositionToChord(pos, stringCount, title) {
|
|
74
|
+
const fingers = [];
|
|
75
|
+
const barreFrets = new Set(pos.barres);
|
|
76
|
+
pos.frets.forEach((fret, dbIdx) => {
|
|
77
|
+
// chords-db lists strings low→high; svguitar numbers strings high→low.
|
|
78
|
+
const stringNum = stringCount - dbIdx;
|
|
79
|
+
if (fret === -1) {
|
|
80
|
+
fingers.push([stringNum, "x"]);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (fret === 0) {
|
|
84
|
+
fingers.push([stringNum, 0]);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Skip strings covered by a barre — the barre draws them.
|
|
88
|
+
if (barreFrets.has(fret))
|
|
89
|
+
return;
|
|
90
|
+
const fingerNum = pos.fingers[dbIdx];
|
|
91
|
+
const text = fingerNum && fingerNum > 0 ? String(fingerNum) : undefined;
|
|
92
|
+
fingers.push(text ? [stringNum, fret, text] : [stringNum, fret]);
|
|
93
|
+
});
|
|
94
|
+
const barres = pos.barres.map((fret) => {
|
|
95
|
+
const playingIdxs = pos.frets
|
|
96
|
+
.map((f, i) => (f === fret ? i : -1))
|
|
97
|
+
.filter((i) => i >= 0);
|
|
98
|
+
const stringNums = playingIdxs.map((i) => stringCount - i);
|
|
99
|
+
const fromString = Math.max(...stringNums);
|
|
100
|
+
const toString = Math.min(...stringNums);
|
|
101
|
+
const fingerNums = playingIdxs
|
|
102
|
+
.map((i) => pos.fingers[i])
|
|
103
|
+
.filter((n) => typeof n === "number" && n > 0);
|
|
104
|
+
const fingerLabel = fingerNums.length ? String(Math.min(...fingerNums)) : undefined;
|
|
105
|
+
return { fromString, toString, fret, ...(fingerLabel ? { text: fingerLabel } : {}) };
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
fingers,
|
|
109
|
+
barres,
|
|
110
|
+
position: pos.baseFret,
|
|
111
|
+
...(title ? { title } : {}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=instruments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instruments.js","sourceRoot":"","sources":["../src/instruments.ts"],"names":[],"mappings":"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"}
|
package/dist/pitch.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fret → pitch derivation.
|
|
3
|
+
*
|
|
4
|
+
* chords-db ships a `midi` array per position, but scripts/build-data.mjs
|
|
5
|
+
* strips it from the shipped JSON to cut bundle weight. Deriving rather than
|
|
6
|
+
* reviving keeps one pitch code path: the candidate szaza merge source ships
|
|
7
|
+
* no midi field at all, so derivation is required regardless.
|
|
8
|
+
*/
|
|
9
|
+
import type { ChordsDbPosition } from "./instruments.js";
|
|
10
|
+
/**
|
|
11
|
+
* Sounding MIDI pitches for a chords-db position.
|
|
12
|
+
*
|
|
13
|
+
* `frets` values are relative to `baseFret`, not absolute fret numbers: a
|
|
14
|
+
* value of 1 at baseFret 3 sounds fret 3. Muted strings (-1) are omitted;
|
|
15
|
+
* open strings (0) sound the open pitch regardless of the diagram window.
|
|
16
|
+
*
|
|
17
|
+
* `capo` is deliberately ignored — it is a rendering hint (draw a bar), not a
|
|
18
|
+
* pitch offset. All 901 capo-flagged guitar positions reproduce chords-db's
|
|
19
|
+
* own `midi` array exactly without it; see test/pitch-golden.test.ts.
|
|
20
|
+
*
|
|
21
|
+
* @param pos - chords-db position
|
|
22
|
+
* @param openMidi - open-string MIDI in chords-db index order (InstrumentConfig.openMidi)
|
|
23
|
+
* @returns sounding pitches only, in chords-db string order
|
|
24
|
+
*/
|
|
25
|
+
export declare function positionToMidi(pos: ChordsDbPosition, openMidi: number[]): number[];
|
|
26
|
+
/**
|
|
27
|
+
* Pitch class of a chord label's root, or null when unparseable.
|
|
28
|
+
*
|
|
29
|
+
* Derived from the requested label rather than from a chords-db `key` field:
|
|
30
|
+
* chords-db spells the same root inconsistently ("Csharp" as a container key,
|
|
31
|
+
* "C#" on the entry), and the label is what the caller actually asked for.
|
|
32
|
+
*/
|
|
33
|
+
export declare function rootPitchClass(label: string): number | null;
|
package/dist/pitch.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sounding MIDI pitches for a chords-db position.
|
|
3
|
+
*
|
|
4
|
+
* `frets` values are relative to `baseFret`, not absolute fret numbers: a
|
|
5
|
+
* value of 1 at baseFret 3 sounds fret 3. Muted strings (-1) are omitted;
|
|
6
|
+
* open strings (0) sound the open pitch regardless of the diagram window.
|
|
7
|
+
*
|
|
8
|
+
* `capo` is deliberately ignored — it is a rendering hint (draw a bar), not a
|
|
9
|
+
* pitch offset. All 901 capo-flagged guitar positions reproduce chords-db's
|
|
10
|
+
* own `midi` array exactly without it; see test/pitch-golden.test.ts.
|
|
11
|
+
*
|
|
12
|
+
* @param pos - chords-db position
|
|
13
|
+
* @param openMidi - open-string MIDI in chords-db index order (InstrumentConfig.openMidi)
|
|
14
|
+
* @returns sounding pitches only, in chords-db string order
|
|
15
|
+
*/
|
|
16
|
+
export function positionToMidi(pos, openMidi) {
|
|
17
|
+
const out = [];
|
|
18
|
+
pos.frets.forEach((fret, i) => {
|
|
19
|
+
if (fret === -1)
|
|
20
|
+
return;
|
|
21
|
+
const open = openMidi[i];
|
|
22
|
+
if (open === undefined)
|
|
23
|
+
return;
|
|
24
|
+
out.push(fret === 0 ? open : open + pos.baseFret + fret - 1);
|
|
25
|
+
});
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
const PITCH_CLASSES = {
|
|
29
|
+
C: 0, "B#": 0,
|
|
30
|
+
"C#": 1, Db: 1,
|
|
31
|
+
D: 2,
|
|
32
|
+
"D#": 3, Eb: 3,
|
|
33
|
+
E: 4, Fb: 4,
|
|
34
|
+
F: 5, "E#": 5,
|
|
35
|
+
"F#": 6, Gb: 6,
|
|
36
|
+
G: 7,
|
|
37
|
+
"G#": 8, Ab: 8,
|
|
38
|
+
A: 9,
|
|
39
|
+
"A#": 10, Bb: 10,
|
|
40
|
+
B: 11, Cb: 11,
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Pitch class of a chord label's root, or null when unparseable.
|
|
44
|
+
*
|
|
45
|
+
* Derived from the requested label rather than from a chords-db `key` field:
|
|
46
|
+
* chords-db spells the same root inconsistently ("Csharp" as a container key,
|
|
47
|
+
* "C#" on the entry), and the label is what the caller actually asked for.
|
|
48
|
+
*/
|
|
49
|
+
export function rootPitchClass(label) {
|
|
50
|
+
const m = label.trim().match(/^([A-Ga-g][#b]?)/);
|
|
51
|
+
if (!m)
|
|
52
|
+
return null;
|
|
53
|
+
const root = m[1].charAt(0).toUpperCase() + m[1].slice(1);
|
|
54
|
+
const pc = PITCH_CLASSES[root];
|
|
55
|
+
return pc === undefined ? null : pc;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=pitch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pitch.js","sourceRoot":"","sources":["../src/pitch.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,GAAqB,EAAE,QAAkB;IACtE,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QAC5B,IAAI,IAAI,KAAK,CAAC,CAAC;YAAE,OAAO;QACxB,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO;QAC/B,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,aAAa,GAA2B;IAC5C,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;IACb,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACd,CAAC,EAAE,CAAC;IACJ,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACd,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACX,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;IACb,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACd,CAAC,EAAE,CAAC;IACJ,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACd,CAAC,EAAE,CAAC;IACJ,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;IAChB,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;CACd,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACjD,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Movable power-chord (root + fifth + octave) shapes.
|
|
3
|
+
*
|
|
4
|
+
* chords-db has no power-chord suffix, so these cannot be looked up. They also
|
|
5
|
+
* need no curation: the shape is perfectly regular, so it derives from the
|
|
6
|
+
* instrument's open-string pitches. Output goes through dbPositionToChord so
|
|
7
|
+
* renderers see the same contract as any other shape.
|
|
8
|
+
*/
|
|
9
|
+
import type { Chord } from "svguitar";
|
|
10
|
+
import type { ChordsDbPosition } from "./instruments.js";
|
|
11
|
+
/** Which string carries the root. "E" = 6th string, "A" = 5th string. */
|
|
12
|
+
export type PowerChordStringSet = "E" | "A";
|
|
13
|
+
export interface PowerChordOptions {
|
|
14
|
+
stringSet: PowerChordStringSet;
|
|
15
|
+
/** Include the octave doubling on the next string up. Default true. */
|
|
16
|
+
includeOctave?: boolean;
|
|
17
|
+
title?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Build a chords-db-shaped position for a power chord.
|
|
21
|
+
*
|
|
22
|
+
* @param rootPc - root pitch class, 0 = C
|
|
23
|
+
* @returns null when rootPc is out of range
|
|
24
|
+
*/
|
|
25
|
+
export declare function powerChordPosition(rootPc: number, opts: PowerChordOptions): ChordsDbPosition | null;
|
|
26
|
+
/** The same shape as an svguitar Chord, ready to render. */
|
|
27
|
+
export declare function powerChordShape(rootPc: number, opts: PowerChordOptions): Chord | null;
|