tonal-guitar 0.1.0 → 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/README.md +157 -14
- package/dist/index.d.mts +375 -10
- package/dist/index.d.ts +375 -10
- package/dist/index.js +1039 -175
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +997 -157
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
} from "tonal-guitar";
|
|
41
41
|
|
|
42
42
|
// 1. Get a shape and build a scale on the fretboard
|
|
43
|
-
const shape = get("
|
|
43
|
+
const shape = get("E Shape");
|
|
44
44
|
const scale = buildFrettedScale(shape, "A");
|
|
45
45
|
|
|
46
46
|
// 2. Generate a pattern and walk it
|
|
@@ -52,8 +52,20 @@ console.log(toAsciiTab(notes));
|
|
|
52
52
|
console.log(toAlphaTeX(notes, { tempo: 100, duration: 8 }));
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
## v0.2.0 Changes
|
|
56
|
+
|
|
57
|
+
- **`buildFromScale` pitch-correctness fix.** `buildFromScale` now relabels a shape into the requested scale's interval frame before building, so minor-tonic (and other non-major) scale names produce correct pitch content instead of silently building the shape's original (usually major) frame at the new tonic. See [buildFromScale](#tonaljs-integration) for the before/after example.
|
|
58
|
+
- **`isShapeCompatible` chroma-set semantics.** Compatibility is now computed via interval-chroma-set coverage (enharmonic-safe) instead of raw interval-string comparison. This does not loosen relative-major/minor matching -- a major-frame shape is still incompatible with a minor scale name.
|
|
59
|
+
- **New APIs:** `relabelShape` (pure tier, `src/transform.ts`) and `relabelShapeToScale` (integration tier) — see [Shape Relabeling](#shape-relabeling).
|
|
60
|
+
- **New `ScaleShape` fields:** optional `quality` and `parentShape`, set on shapes derived via `relabelShape`.
|
|
61
|
+
- **10 new registered entries:** 5 minor CAGED scale shapes and 5 minor pentatonic boxes — see [Minor-Quality Entries](#minor-quality-entries).
|
|
62
|
+
|
|
63
|
+
See [CHANGELOG.md](./CHANGELOG.md) for full details.
|
|
64
|
+
|
|
55
65
|
## API
|
|
56
66
|
|
|
67
|
+
For automated chord/scale shape correctness and quality checks, see [Audit](docs/api/audit.md) — also viewable live on the deployed Guitar Lab [`/shapes`](https://theguitarstudio.github.io/tonal-guitar/shapes) page.
|
|
68
|
+
|
|
57
69
|
### Tuning Constants
|
|
58
70
|
|
|
59
71
|
Pre-defined tunings as `string[]` arrays (low string to high string):
|
|
@@ -125,15 +137,15 @@ fretboard(STANDARD, [0, 4]); // every note on strings 0-5, frets 0-4
|
|
|
125
137
|
|
|
126
138
|
### Shape Registry
|
|
127
139
|
|
|
128
|
-
Built-in shapes are registered at import time: CAGED scale shapes (5), CAGED chord shapes (5), 3NPS patterns (7), and pentatonic boxes (
|
|
140
|
+
Built-in shapes are registered at import time: CAGED scale shapes (5), CAGED chord shapes (5), 3NPS patterns (7), pentatonic boxes (5) -- plus (v0.2.0) 5 minor CAGED scale shapes and 5 minor pentatonic boxes, derived from the major-frame shapes via `relabelShape`. See [Minor-Quality Entries](#minor-quality-entries) below.
|
|
129
141
|
|
|
130
142
|
#### `get(name: string) => ScaleShape | undefined`
|
|
131
143
|
|
|
132
144
|
Retrieve a scale shape by name:
|
|
133
145
|
|
|
134
146
|
```js
|
|
135
|
-
get("
|
|
136
|
-
get("3NPS Pattern 1"); // => { name: "3NPS Pattern 1", system: "3nps", ... }
|
|
147
|
+
get("E Shape"); // => { name: "E Shape", system: "caged", strings: [...], ... }
|
|
148
|
+
get("3NPS Pattern 1 (Ionian)"); // => { name: "3NPS Pattern 1 (Ionian)", system: "3nps", ... }
|
|
137
149
|
```
|
|
138
150
|
|
|
139
151
|
#### `all() => ScaleShape[]`
|
|
@@ -145,7 +157,7 @@ Get all registered scale shapes.
|
|
|
145
157
|
Get all registered scale shape names:
|
|
146
158
|
|
|
147
159
|
```js
|
|
148
|
-
names(); // => ["
|
|
160
|
+
names(); // => ["E Shape", "D Shape", "C Shape", ...]
|
|
149
161
|
```
|
|
150
162
|
|
|
151
163
|
#### `add(shape: ScaleShape) => ScaleShape`
|
|
@@ -176,13 +188,13 @@ Separate registry for chord shapes with the same API: `chordShapes.get()`, `chor
|
|
|
176
188
|
Apply a scale shape to a root note and tuning, returning all fretted positions:
|
|
177
189
|
|
|
178
190
|
```js
|
|
179
|
-
const scale = buildFrettedScale(get("
|
|
191
|
+
const scale = buildFrettedScale(get("E Shape"), "A");
|
|
180
192
|
// => {
|
|
181
193
|
// empty: false,
|
|
182
194
|
// root: "A",
|
|
183
195
|
// scaleType: "",
|
|
184
196
|
// scaleName: "",
|
|
185
|
-
// shapeName: "
|
|
197
|
+
// shapeName: "E Shape",
|
|
186
198
|
// tuning: ["E2", "A2", "D3", "G3", "B3", "E4"],
|
|
187
199
|
// notes: [
|
|
188
200
|
// { string: 0, fret: 5, note: "A2", pc: "A", interval: "1P", degree: 1, midi: 45, ... },
|
|
@@ -220,6 +232,66 @@ const chord = applyChordShape(chordShapes.get("E Shape"), "A");
|
|
|
220
232
|
// }
|
|
221
233
|
```
|
|
222
234
|
|
|
235
|
+
### Shape Relabeling
|
|
236
|
+
|
|
237
|
+
#### `relabelShape(shape: ScaleShape, targetIntervals: string[], options?: RelabelOptions) => ScaleShape | undefined`
|
|
238
|
+
|
|
239
|
+
Pure-tier primitive that rewrites a shape's per-string interval labels into a different, rotation-compatible interval frame (e.g. turning a major-frame CAGED shape into its natural-minor labeling). Geometry (which string/fret each note lands on) is unchanged -- only `strings` labels, `rootString`, `name`, `quality`, and `parentShape` change. Returns a new `ScaleShape` (the input is never mutated), or `undefined` when no valid relabeling exists (e.g. relabeling a 7-note shape into a 5-note pentatonic frame).
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
import { relabelShape, get } from "tonal-guitar";
|
|
243
|
+
|
|
244
|
+
const em = relabelShape(get("G Shape"), ["1P", "2M", "3m", "4P", "5P", "6m", "7m"], {
|
|
245
|
+
name: "Em Shape",
|
|
246
|
+
quality: "minor",
|
|
247
|
+
parentShape: "G Shape",
|
|
248
|
+
});
|
|
249
|
+
// em.rootString === 0, em.quality === "minor", em.parentShape === "G Shape"
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
interface RelabelOptions {
|
|
254
|
+
name?: string; // override the derived name (default: input shape.name)
|
|
255
|
+
quality?: string; // value written to result.quality
|
|
256
|
+
parentShape?: string; // value written to result.parentShape (default: shape.name)
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
This is the primitive used to derive the 10 registered minor-quality entries -- see [Minor-Quality Entries](#minor-quality-entries) below, and `relabelShapeToScale` (in [Tonal.js Integration](#tonaljs-integration)) for the scale-name-driven wrapper.
|
|
261
|
+
|
|
262
|
+
### Minor-Quality Entries
|
|
263
|
+
|
|
264
|
+
The 5 CAGED and 5 pentatonic major-frame source shapes are each relabeled at import time (via `relabelShape`) into a paired minor-frame entry, registered under a distinct name. Geometry is identical to the parent shape -- only interval labels, `rootString`, and `quality`/`parentShape` metadata differ.
|
|
265
|
+
|
|
266
|
+
#### Minor CAGED shapes
|
|
267
|
+
|
|
268
|
+
| Parent shape | Registered minor name | `rootString` | `quality` |
|
|
269
|
+
| --- | --- | --- | --- |
|
|
270
|
+
| `"E Shape"` | `"Dm Shape"` | 2 | `"minor"` |
|
|
271
|
+
| `"D Shape"` | `"Cm Shape"` | 1 | `"minor"` |
|
|
272
|
+
| `"C Shape"` | `"Am Shape"` | 1 | `"minor"` |
|
|
273
|
+
| `"A Shape"` | `"Gm Shape"` | 0 | `"minor"` |
|
|
274
|
+
| `"G Shape"` | `"Em Shape"` | 0 | `"minor"` |
|
|
275
|
+
|
|
276
|
+
#### Minor pentatonic boxes
|
|
277
|
+
|
|
278
|
+
| Parent shape | Registered minor name | `rootString` | `quality` |
|
|
279
|
+
| --- | --- | --- | --- |
|
|
280
|
+
| `"Pentatonic Box 1"` | `"Pentatonic Box 1 Minor"` | 0 | `"minor-pentatonic"` |
|
|
281
|
+
| `"Pentatonic Box 2"` | `"Pentatonic Box 2 Minor"` | 2 | `"minor-pentatonic"` |
|
|
282
|
+
| `"Pentatonic Box 3"` | `"Pentatonic Box 3 Minor"` | 1 | `"minor-pentatonic"` |
|
|
283
|
+
| `"Pentatonic Box 4"` | `"Pentatonic Box 4 Minor"` | 1 | `"minor-pentatonic"` |
|
|
284
|
+
| `"Pentatonic Box 5"` | `"Pentatonic Box 5 Minor"` | 0 | `"minor-pentatonic"` |
|
|
285
|
+
|
|
286
|
+
```js
|
|
287
|
+
get("Em Shape");
|
|
288
|
+
// => { name: "Em Shape", system: "caged", quality: "minor", parentShape: "G Shape", rootString: 0, ... }
|
|
289
|
+
|
|
290
|
+
// Same fret positions as the major-frame parent at the relative-major root:
|
|
291
|
+
buildFrettedScale(get("G Shape"), "C").notes;
|
|
292
|
+
buildFrettedScale(get("Em Shape"), "A").notes; // same {string, fret} pairs, A=1P, C=3m
|
|
293
|
+
```
|
|
294
|
+
|
|
223
295
|
### Pattern Generators
|
|
224
296
|
|
|
225
297
|
All generators return `number[]` degree sequences.
|
|
@@ -278,7 +350,7 @@ sixths(7); // same as ascendingIntervals(7, 5)
|
|
|
278
350
|
Walk a degree pattern through a fretted scale, picking concrete notes:
|
|
279
351
|
|
|
280
352
|
```js
|
|
281
|
-
const scale = buildFrettedScale(get("
|
|
353
|
+
const scale = buildFrettedScale(get("E Shape"), "A");
|
|
282
354
|
const notes = walkPattern(scale, [1, 3, 5, 7, 6, 5, 4, 3, 2, 1]);
|
|
283
355
|
```
|
|
284
356
|
|
|
@@ -502,13 +574,36 @@ These functions require optional peer dependencies (`@tonaljs/scale`, `@tonaljs/
|
|
|
502
574
|
Build a fretted scale using Tonal's `Scale.get()` for validation. Populates `scaleType` and `scaleName`:
|
|
503
575
|
|
|
504
576
|
```js
|
|
505
|
-
const scale = buildFromScale(get("
|
|
577
|
+
const scale = buildFromScale(get("E Shape"), "A major");
|
|
506
578
|
scale.scaleType; // => "major"
|
|
507
579
|
scale.scaleName; // => "A major"
|
|
508
580
|
|
|
509
581
|
buildFromScale(get("Pentatonic Box 1"), "A minor pentatonic");
|
|
510
582
|
```
|
|
511
583
|
|
|
584
|
+
> **v0.2.0 behavior change (pitch-correctness fix).** `buildFromScale` now relabels `shape` into `scaleName`'s interval frame (via `relabelShape`) before building. Previously, `buildFromScale(get("E Shape"), "A minor")` applied the major-frame shape as-is at the "A" tonic, silently producing **A-major pitch classes** tagged `scaleType: "aeolian"`. As of v0.2.0 the same call produces correct A-natural-minor notes (`A=1P`, `C=3m`, `E=5P`) in the relabeled geometry. If the shape can't be relabeled into the requested frame, `relabelShape` returns `undefined` and `buildFromScale` falls back to its pre-fix behavior (building the shape as-is), so no previously-working call regresses to empty. See [Shape Relabeling](#shape-relabeling) above.
|
|
585
|
+
|
|
586
|
+
```js
|
|
587
|
+
// v0.2.0: minor tonics now produce correct pitch content --
|
|
588
|
+
// pre-fix this call produced A-major pitch classes (A, B, C#, D, E, F#, G#)
|
|
589
|
+
// mislabeled as "aeolian"; post-fix it produces A-natural-minor pitch classes.
|
|
590
|
+
const aMinor = buildFromScale(get("E Shape"), "A minor");
|
|
591
|
+
[...new Set(aMinor.notes.map((n) => n.pc))].sort();
|
|
592
|
+
// => ["A", "B", "C", "D", "E", "F", "G"]
|
|
593
|
+
aMinor.notes.find((n) => n.pc === "C").interval; // => "3m"
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
#### `relabelShapeToScale(shape: ScaleShape, scaleName: string, options?: RelabelOptions) => ScaleShape | undefined`
|
|
597
|
+
|
|
598
|
+
Integration-tier wrapper over the pure `relabelShape` primitive (see [Shape Relabeling](#shape-relabeling)): resolves `scaleName` via Tonal's `Scale.get()` and relabels `shape` into that scale's interval frame. Enharmonic spelling is inherited from the target scale's own intervals.
|
|
599
|
+
|
|
600
|
+
```js
|
|
601
|
+
relabelShapeToScale(get("G Shape"), "A minor", { name: "Em Shape", quality: "minor", parentShape: "G Shape" });
|
|
602
|
+
// equals the pre-registered get("Em Shape")
|
|
603
|
+
|
|
604
|
+
relabelShapeToScale(get("E Shape"), "bogus scale"); // => undefined
|
|
605
|
+
```
|
|
606
|
+
|
|
512
607
|
#### `relatedScales(frettedScale: FrettedScale) => Array<{ root: string, scale: string }>`
|
|
513
608
|
|
|
514
609
|
Find all modal relatives (scales sharing the same pitch classes):
|
|
@@ -550,20 +645,67 @@ Returns `{ empty: true }` if the chord is not found in the key.
|
|
|
550
645
|
|
|
551
646
|
#### `isShapeCompatible(shape: ScaleShape, scaleName: string) => boolean`
|
|
552
647
|
|
|
553
|
-
Check
|
|
648
|
+
Check whether a shape is compatible with a scale by interval-chroma coverage (root-relative, enharmonic-safe): a shape is compatible iff its chroma set is a non-empty subset of the scale's chroma set.
|
|
649
|
+
|
|
650
|
+
> **v0.2.0 behavior change.** The comparison is now chroma-based rather than raw interval-string comparison (fixes enharmonic spelling mismatches like `4A` vs `5d` across Tonal scale types). This is **not** a relative-major/minor loosening -- a major-frame shape is still incompatible with a minor scale name. The relative-major/minor identity is expressed through the registered minor-quality entries instead (see [Minor-Quality Entries](#minor-quality-entries)).
|
|
554
651
|
|
|
555
652
|
```js
|
|
556
|
-
isShapeCompatible(get("
|
|
653
|
+
isShapeCompatible(get("E Shape"), "A major"); // => true
|
|
557
654
|
isShapeCompatible(get("Pentatonic Box 1"), "A major"); // => true (subset)
|
|
655
|
+
isShapeCompatible(get("E Shape"), "A minor"); // => false (major frame ⊄ minor frame, root-relative)
|
|
656
|
+
isShapeCompatible(get("Em Shape"), "A minor"); // => true (minor frame ⊆ minor frame)
|
|
657
|
+
isShapeCompatible(get("Pentatonic Box 1 Minor"), "A minor"); // => true (minor-pent ⊆ natural-minor chromas)
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
#### `scalesContainingChord(chord: string, options?: ScalesContainingChordOptions) => ScalesContainingChordResult`
|
|
661
|
+
|
|
662
|
+
Find scales that contain a chord's tones -- "what can I play over this chord". Resolves `chord` via Tonal's `Chord.get()`, then sweeps 12 chromatic roots x a fixed corpus of 11 scale types (`DEFAULT_SCALE_CORPUS`: major, dorian, phrygian, lydian, mixolydian, aeolian, locrian, harmonic minor, melodic minor, major pentatonic, minor pentatonic), keeping every candidate whose pitch-class set is a (tolerant) superset of the chord's. Results are partitioned into `rootAnchored` (scale tonic matches the chord's root) and `otherRoots` (every other root), each ranked by fit (`extraTones` ascending, then corpus order, then root-chroma distance, then name).
|
|
663
|
+
|
|
664
|
+
```js
|
|
665
|
+
import { scalesContainingChord } from "tonal-guitar";
|
|
666
|
+
|
|
667
|
+
const result = scalesContainingChord("Cmaj7");
|
|
668
|
+
result.chord; // => "Cmaj7"
|
|
669
|
+
result.root; // => "C"
|
|
670
|
+
result.rootAnchored.map((s) => s.name);
|
|
671
|
+
// => ["C major", "C lydian"]
|
|
672
|
+
// (C mixolydian and C dorian are excluded -- they don't contain the chord's B / E natural)
|
|
673
|
+
|
|
674
|
+
scalesContainingChord("Cm7").rootAnchored.map((s) => s.name);
|
|
675
|
+
// => ["C minor pentatonic", "C dorian", "C phrygian", "C minor"]
|
|
676
|
+
// ("C major" is excluded -- it doesn't contain the chord's Eb / Bb)
|
|
558
677
|
```
|
|
559
678
|
|
|
679
|
+
Containment is strict by default (`tolerateMissing: 0`). Pass `tolerateMissing: N` to admit scales missing up to `N` chord tones, recorded in each result's `omittedTones`:
|
|
680
|
+
|
|
681
|
+
```js
|
|
682
|
+
const tolerant = scalesContainingChord("Cm7", { tolerateMissing: 1 });
|
|
683
|
+
tolerant.rootAnchored.find((s) => s.name === "C mixolydian").omittedTones; // => ["Eb"]
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
Never throws -- an unresolvable or empty chord name returns `{ chord: "", root: "", rootAnchored: [], otherRoots: [] }`.
|
|
687
|
+
|
|
688
|
+
> **Note on "aeolian" naming.** `DEFAULT_SCALE_CORPUS` includes `"aeolian"`, but `@tonaljs/scale` normalizes that alias to its canonical dictionary entry (`Scale.get("A aeolian")` returns `{ type: "minor", name: "A minor", ... }` -- chroma-identical, just relabeled). As a result, aeolian-swept candidates surface in the output as e.g. `"A minor"`, not `"A aeolian"`.
|
|
689
|
+
|
|
560
690
|
#### `modeShapes(modeName: string, shapeSystem?: string) => ScaleShape[]`
|
|
561
691
|
|
|
562
692
|
Get all registered shapes compatible with a mode/scale:
|
|
563
693
|
|
|
564
694
|
```js
|
|
565
|
-
modeShapes("
|
|
566
|
-
|
|
695
|
+
modeShapes("C major", "caged"); // => 5 (major-frame CAGED shapes; minor entries excluded)
|
|
696
|
+
|
|
697
|
+
// v0.2.0: minor tonics now resolve to the 10 registered minor-quality entries
|
|
698
|
+
modeShapes("A minor", "caged");
|
|
699
|
+
// => 5: "Dm Shape", "Cm Shape", "Am Shape", "Gm Shape", "Em Shape"
|
|
700
|
+
modeShapes("A minor pentatonic", "pentatonic");
|
|
701
|
+
// => the 5 "Pentatonic Box N Minor" entries
|
|
702
|
+
modeShapes("A minor").length; // => 10 (5 minor CAGED + 5 minor pentatonic)
|
|
703
|
+
|
|
704
|
+
// Chroma-subset coverage applies to any superset frame: the minor-pentatonic
|
|
705
|
+
// chroma set {0,3,5,7,10} is also a subset of dorian's {0,2,3,5,7,9,10}, so
|
|
706
|
+
modeShapes("A dorian");
|
|
707
|
+
// => the 5 "Pentatonic Box N Minor" entries (there is no first-class dorian entry --
|
|
708
|
+
// this is the minor-pentatonic entries' chroma set fitting dorian too)
|
|
567
709
|
```
|
|
568
710
|
|
|
569
711
|
## Types
|
|
@@ -572,8 +714,9 @@ The package exports these TypeScript interfaces:
|
|
|
572
714
|
|
|
573
715
|
- `FrettedNote` — a note on the fretboard with position, pitch, and interval info
|
|
574
716
|
- `FrettedScale` — a scale applied to the fretboard (with `empty` sentinel pattern)
|
|
575
|
-
- `ScaleShape` — a scale shape definition (intervals per string)
|
|
717
|
+
- `ScaleShape` — a scale shape definition (intervals per string, plus optional `quality`/`parentShape` metadata set on entries derived via `relabelShape`)
|
|
576
718
|
- `ChordShape` — a chord voicing definition (one interval per string + fingerings + optional harmonic metadata)
|
|
719
|
+
- `RelabelOptions` — options for `relabelShape`/`relabelShapeToScale` (`name`, `quality`, `parentShape`)
|
|
577
720
|
- `Barre` — barre chord finger placement
|
|
578
721
|
- `Fingering` — chord shape applied to a specific root
|
|
579
722
|
- `FretboardPosition` — absolute position on the fretboard (string, fret, note, midi)
|