@pepperhorn/chordl-core 0.3.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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/dist/audio/midi-export.d.ts +23 -0
  3. package/dist/audio/midi-export.js +155 -0
  4. package/dist/audio/midi-export.js.map +1 -0
  5. package/dist/chord-sheet/codec.d.ts +14 -0
  6. package/dist/chord-sheet/codec.js +119 -0
  7. package/dist/chord-sheet/codec.js.map +1 -0
  8. package/dist/chord-sheet/defaults.d.ts +13 -0
  9. package/dist/chord-sheet/defaults.js +48 -0
  10. package/dist/chord-sheet/defaults.js.map +1 -0
  11. package/dist/chord-sheet/index.d.ts +3 -0
  12. package/dist/chord-sheet/index.js +4 -0
  13. package/dist/chord-sheet/index.js.map +1 -0
  14. package/dist/chord-sheet/schema.d.ts +6 -0
  15. package/dist/chord-sheet/schema.js +17 -0
  16. package/dist/chord-sheet/schema.js.map +1 -0
  17. package/dist/config.d.ts +2 -0
  18. package/dist/config.js +15 -0
  19. package/dist/config.js.map +1 -0
  20. package/dist/engine/auto-fingering.d.ts +36 -0
  21. package/dist/engine/auto-fingering.js +344 -0
  22. package/dist/engine/auto-fingering.js.map +1 -0
  23. package/dist/engine/highlight-mapper.d.ts +3 -0
  24. package/dist/engine/highlight-mapper.js +46 -0
  25. package/dist/engine/highlight-mapper.js.map +1 -0
  26. package/dist/engine/keyboard-layout.d.ts +6 -0
  27. package/dist/engine/keyboard-layout.js +65 -0
  28. package/dist/engine/keyboard-layout.js.map +1 -0
  29. package/dist/engine/note-spelling.d.ts +54 -0
  30. package/dist/engine/note-spelling.js +103 -0
  31. package/dist/engine/note-spelling.js.map +1 -0
  32. package/dist/engine/scale-fingering.d.ts +21 -0
  33. package/dist/engine/scale-fingering.js +130 -0
  34. package/dist/engine/scale-fingering.js.map +1 -0
  35. package/dist/engine/staff-constants.d.ts +16 -0
  36. package/dist/engine/staff-constants.js +20 -0
  37. package/dist/engine/staff-constants.js.map +1 -0
  38. package/dist/engine/staff-glyphs.d.ts +17 -0
  39. package/dist/engine/staff-glyphs.js +59 -0
  40. package/dist/engine/staff-glyphs.js.map +1 -0
  41. package/dist/engine/staff-layout.d.ts +28 -0
  42. package/dist/engine/staff-layout.js +238 -0
  43. package/dist/engine/staff-layout.js.map +1 -0
  44. package/dist/engine/svg-constants.d.ts +19 -0
  45. package/dist/engine/svg-constants.js +40 -0
  46. package/dist/engine/svg-constants.js.map +1 -0
  47. package/dist/index.d.ts +42 -0
  48. package/dist/index.js +37 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/logging.d.ts +70 -0
  51. package/dist/logging.js +24 -0
  52. package/dist/logging.js.map +1 -0
  53. package/dist/parser/natural-language.d.ts +9 -0
  54. package/dist/parser/natural-language.js +406 -0
  55. package/dist/parser/natural-language.js.map +1 -0
  56. package/dist/parser/progression-parser.d.ts +16 -0
  57. package/dist/parser/progression-parser.js +97 -0
  58. package/dist/parser/progression-parser.js.map +1 -0
  59. package/dist/pipeline.d.ts +33 -0
  60. package/dist/pipeline.js +203 -0
  61. package/dist/pipeline.js.map +1 -0
  62. package/dist/progression/form-templates.d.ts +11 -0
  63. package/dist/progression/form-templates.js +105 -0
  64. package/dist/progression/form-templates.js.map +1 -0
  65. package/dist/progression/index.d.ts +5 -0
  66. package/dist/progression/index.js +4 -0
  67. package/dist/progression/index.js.map +1 -0
  68. package/dist/progression/progression-resolver.d.ts +29 -0
  69. package/dist/progression/progression-resolver.js +135 -0
  70. package/dist/progression/progression-resolver.js.map +1 -0
  71. package/dist/progression/roman-numeral.d.ts +14 -0
  72. package/dist/progression/roman-numeral.js +78 -0
  73. package/dist/progression/roman-numeral.js.map +1 -0
  74. package/dist/resolver/auto-layout.d.ts +20 -0
  75. package/dist/resolver/auto-layout.js +195 -0
  76. package/dist/resolver/auto-layout.js.map +1 -0
  77. package/dist/resolver/chord-resolver.d.ts +8 -0
  78. package/dist/resolver/chord-resolver.js +172 -0
  79. package/dist/resolver/chord-resolver.js.map +1 -0
  80. package/dist/resolver/scale-resolver.d.ts +9 -0
  81. package/dist/resolver/scale-resolver.js +45 -0
  82. package/dist/resolver/scale-resolver.js.map +1 -0
  83. package/dist/themes/boomwhacker.d.ts +2 -0
  84. package/dist/themes/boomwhacker.js +35 -0
  85. package/dist/themes/boomwhacker.js.map +1 -0
  86. package/dist/themes/crf.d.ts +19 -0
  87. package/dist/themes/crf.js +34 -0
  88. package/dist/themes/crf.js.map +1 -0
  89. package/dist/themes/index.d.ts +3 -0
  90. package/dist/themes/index.js +25 -0
  91. package/dist/themes/index.js.map +1 -0
  92. package/dist/themes/simple.d.ts +2 -0
  93. package/dist/themes/simple.js +13 -0
  94. package/dist/themes/simple.js.map +1 -0
  95. package/dist/theory/chord-tones.d.ts +56 -0
  96. package/dist/theory/chord-tones.js +215 -0
  97. package/dist/theory/chord-tones.js.map +1 -0
  98. package/dist/theory/degree-labels.d.ts +31 -0
  99. package/dist/theory/degree-labels.js +74 -0
  100. package/dist/theory/degree-labels.js.map +1 -0
  101. package/dist/types.d.ts +123 -0
  102. package/dist/types.js +2 -0
  103. package/dist/types.js.map +1 -0
  104. package/package.json +37 -0
@@ -0,0 +1,16 @@
1
+ import type { Format } from "../types";
2
+ export interface ParsedProgressionRequest {
3
+ progression: string;
4
+ key: string;
5
+ numExamples: number;
6
+ styleHint?: string;
7
+ format?: Format;
8
+ }
9
+ /**
10
+ * Detect whether an input string is a progression request rather than a single chord.
11
+ */
12
+ export declare function isProgressionRequest(input: string): boolean;
13
+ /**
14
+ * Parse a progression request from natural language.
15
+ */
16
+ export declare function parseProgressionRequest(input: string): ParsedProgressionRequest;
@@ -0,0 +1,97 @@
1
+ import { MAX_EXAMPLES } from "../config";
2
+ // Roman numeral pattern — must not be followed by a letter (prevents "in", "it", "is" etc.)
3
+ const ROMAN_NUMERAL_SRC = `[#b]?(?:iv|vi{1,2}|i{1,3}|IV|VI{1,2}|I{1,3}|V)(?:[°ø]?\\d*(?:maj|m)?[0-9#b]*)(?![a-z])`;
4
+ // A progression: 2+ roman numerals separated by delimiters
5
+ const PROGRESSION_PATTERN = new RegExp(`(?:^|\\s)(${ROMAN_NUMERAL_SRC}(?:[\\s\\-–—→>]+${ROMAN_NUMERAL_SRC})+)`, "i");
6
+ // Example count: "3 examples", "show me 2", "give me 5 examples"
7
+ const EXAMPLE_COUNT_RE = /(\d+)\s*(?:examples?|variations?|versions?|ways?|options?)/i;
8
+ // Style hints
9
+ const STYLE_RE = /(?:(?:in\s+)?(?:the\s+)?style\s+of\s+|like\s+|a\s+la\s+)([\w\s]+?)(?:\s*$|\s*,|\s+(?:in\s|examples?|variations?))/i;
10
+ const STYLE_KEYWORD_RE = /\b(bebop|basie|nestico|ellington|modal|comping|rootless|quartal|block\s*chords?|locked\s*hands|drop\s*2\s*\+?\s*4|drop\s*2|upper\s*structure|shell|stride|spread|4[- ]?note\s*closed|bill\s*evans|mccoy\s*tyner|herbie\s*hancock|bud\s*powell|monk)\b/i;
11
+ // Form template names — must come before roman numeral detection
12
+ const FORM_NAMES = [
13
+ { pattern: /\bjazz\s*blues\b/i, id: "jazz blues" },
14
+ { pattern: /\bblues\b/i, id: "blues" },
15
+ { pattern: /\brhythm\s*changes?\b/i, id: "rhythm changes" },
16
+ { pattern: /\bturnaround\b/i, id: "I-vi-ii-V" },
17
+ { pattern: /\bmodal\s*vamp\b/i, id: "modal vamp" },
18
+ { pattern: /\bbackdoor\b/i, id: "backdoor" },
19
+ { pattern: /\btritone\s*sub(?:stitution)?\b/i, id: "tritone sub" },
20
+ { pattern: /\bminor\s*(?:ii[\s\-–—]*v[\s\-–—]*i|2[\s\-–—]*5[\s\-–—]*1)\b/i, id: "minor ii-V-i" },
21
+ ];
22
+ /**
23
+ * Detect whether an input string is a progression request rather than a single chord.
24
+ */
25
+ export function isProgressionRequest(input) {
26
+ // Check for form template names first
27
+ for (const { pattern } of FORM_NAMES) {
28
+ if (pattern.test(input))
29
+ return true;
30
+ }
31
+ // Check for roman numeral progression pattern (2+ numerals separated by delimiters)
32
+ if (PROGRESSION_PATTERN.test(input))
33
+ return true;
34
+ // Check for "progression" keyword
35
+ if (/\bprogression\b/i.test(input))
36
+ return true;
37
+ return false;
38
+ }
39
+ /**
40
+ * Parse a progression request from natural language.
41
+ */
42
+ export function parseProgressionRequest(input) {
43
+ // Extract example count
44
+ let numExamples = MAX_EXAMPLES;
45
+ const countMatch = input.match(EXAMPLE_COUNT_RE);
46
+ if (countMatch) {
47
+ numExamples = Math.min(parseInt(countMatch[1], 10), MAX_EXAMPLES);
48
+ }
49
+ // Extract style
50
+ let styleHint;
51
+ const styleMatch = input.match(STYLE_RE);
52
+ const styleKeywordMatch = input.match(STYLE_KEYWORD_RE);
53
+ if (styleMatch) {
54
+ styleHint = styleMatch[1].trim();
55
+ }
56
+ else if (styleKeywordMatch) {
57
+ styleHint = styleKeywordMatch[1].trim();
58
+ }
59
+ // Extract format
60
+ let format;
61
+ const formatMatch = input.match(/\b(compact|exact)\b/i);
62
+ if (formatMatch) {
63
+ format = formatMatch[1].toLowerCase();
64
+ }
65
+ // Extract key — "in G", "in the key of Db", "in F#"
66
+ let key = "C";
67
+ const keyMatch = input.match(/\bin\s+(?:the\s+)?(?:key\s+(?:of\s+)?)?([A-Ga-g][#b]?)(?:\s|$)/i);
68
+ if (keyMatch) {
69
+ key = keyMatch[1].charAt(0).toUpperCase() + keyMatch[1].slice(1);
70
+ }
71
+ // Extract progression — check form names first, then roman numerals
72
+ let progression = "";
73
+ for (const { pattern, id } of FORM_NAMES) {
74
+ if (pattern.test(input)) {
75
+ progression = id;
76
+ break;
77
+ }
78
+ }
79
+ // If no form name matched, try roman numeral pattern
80
+ if (!progression) {
81
+ const progMatch = input.match(PROGRESSION_PATTERN);
82
+ if (progMatch) {
83
+ progression = progMatch[1].trim();
84
+ }
85
+ }
86
+ if (!progression) {
87
+ progression = "ii-V-I";
88
+ }
89
+ return {
90
+ progression,
91
+ key,
92
+ numExamples,
93
+ styleHint,
94
+ format,
95
+ };
96
+ }
97
+ //# sourceMappingURL=progression-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progression-parser.js","sourceRoot":"","sources":["../../src/parser/progression-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { LogConfig } from "./logging";
2
+ export interface ChordRequest {
3
+ input: string;
4
+ format?: "json" | "svg" | "midi";
5
+ logConfig?: LogConfig;
6
+ }
7
+ export interface ChordResultKeyboard {
8
+ startFrom: string;
9
+ size: number;
10
+ highlightKeys: string[];
11
+ }
12
+ export interface ChordResultTelemetry {
13
+ parser: "chord" | "progression";
14
+ resolver: {
15
+ root: string;
16
+ type: string;
17
+ pitchClasses: string[];
18
+ };
19
+ durationMs: number;
20
+ }
21
+ export interface ChordResult {
22
+ success: boolean;
23
+ chordName: string;
24
+ root: string;
25
+ notes: string[];
26
+ inversion?: number;
27
+ keyboard: ChordResultKeyboard;
28
+ telemetry: ChordResultTelemetry;
29
+ svg?: string;
30
+ midi?: Uint8Array;
31
+ errorMessage?: string;
32
+ }
33
+ export declare function processChordRequest(request: ChordRequest): ChordResult;
@@ -0,0 +1,203 @@
1
+ import { parseChordDescription } from "./parser/natural-language";
2
+ import { resolveChord } from "./resolver/chord-resolver";
3
+ import { calculateLayout } from "./resolver/auto-layout";
4
+ import { computeKeyboard, computeSvgDimensions } from "./engine/keyboard-layout";
5
+ import { mapHighlights, normalizeNote } from "./engine/highlight-mapper";
6
+ import { generateMidiFile } from "./audio/midi-export";
7
+ import { logChordRequest } from "./logging";
8
+ import { WHITE_KEY_RY, BLACK_KEY_RY, DEFAULT_STROKE, DEFAULT_STROKE_WIDTH, } from "./engine/svg-constants";
9
+ // ── SVG builder ───────────────────────────────────────────────────
10
+ function buildSvgString(keys, fills, svgWidth, svgHeight) {
11
+ const whiteRects = [];
12
+ const blackRects = [];
13
+ for (let i = 0; i < keys.length; i++) {
14
+ const k = keys[i];
15
+ const fill = fills[i];
16
+ const ry = k.isBlack ? BLACK_KEY_RY : WHITE_KEY_RY;
17
+ const rect = `<rect x="${k.x}" y="${k.y}" width="${k.width}" height="${k.height}" rx="${ry}" ry="${ry}" fill="${fill}" stroke="${DEFAULT_STROKE}" stroke-width="${DEFAULT_STROKE_WIDTH}"/>`;
18
+ if (k.isBlack) {
19
+ blackRects.push(rect);
20
+ }
21
+ else {
22
+ whiteRects.push(rect);
23
+ }
24
+ }
25
+ return [
26
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${svgWidth}" height="${svgHeight}" viewBox="0 0 ${svgWidth} ${svgHeight}">`,
27
+ ...whiteRects,
28
+ ...blackRects,
29
+ `</svg>`,
30
+ ].join("\n");
31
+ }
32
+ import { PC_SEMITONES as NOTE_SEMITONES } from "./engine/note-spelling";
33
+ // ── Main pipeline ─────────────────────────────────────────────────
34
+ export function processChordRequest(request) {
35
+ const start = performance.now();
36
+ // Default error result
37
+ const errorResult = (msg) => ({
38
+ success: false,
39
+ chordName: "",
40
+ root: "",
41
+ notes: [],
42
+ keyboard: { startFrom: "C", size: 8, highlightKeys: [] },
43
+ telemetry: {
44
+ parser: "chord",
45
+ resolver: { root: "", type: "", pitchClasses: [] },
46
+ durationMs: performance.now() - start,
47
+ },
48
+ errorMessage: msg,
49
+ });
50
+ try {
51
+ const { input, format: outputFormat, logConfig } = request;
52
+ if (!input || !input.trim()) {
53
+ return errorResult("No chord input provided");
54
+ }
55
+ // 1. Parse
56
+ const parsed = parseChordDescription(input);
57
+ if (!parsed.chordName) {
58
+ return errorResult("Could not extract a chord name from the input");
59
+ }
60
+ // 2. Resolve
61
+ const resolved = resolveChord(parsed.chordName, parsed.inversion);
62
+ const pitchClasses = resolved.notes; // display names (Bb, Eb, etc.)
63
+ const keyboardPitchClasses = pitchClasses.map(normalizeNote); // sharps for keyboard internals
64
+ // 3. Layout
65
+ const layoutFormat = parsed.format ?? "compact";
66
+ const layout = calculateLayout(keyboardPitchClasses, {
67
+ padding: parsed.padding,
68
+ startingNote: parsed.startingNote,
69
+ spanFrom: parsed.spanFrom,
70
+ spanTo: parsed.spanTo,
71
+ });
72
+ // 4. Compute keyboard keys
73
+ const keys = computeKeyboard(layout.startFrom, layout.size, layoutFormat);
74
+ // 5. Build octave-qualified highlight keys
75
+ const baseOctave = 4;
76
+ const highlightKeys = [];
77
+ const midiNoteNames = [];
78
+ // Max playable hand span: octave + fifth (~19 semitones)
79
+ const MAX_SPAN = 28;
80
+ // Build ascending semitone sequence for the chord notes
81
+ let prevSemitone = -1;
82
+ let currentOctave = baseOctave + layout.chordOctave;
83
+ // Step 1: naive ascending octave assignment
84
+ const assigned = [];
85
+ for (let i = 0; i < pitchClasses.length; i++) {
86
+ const pc = pitchClasses[i];
87
+ const kpc = keyboardPitchClasses[i];
88
+ const semitone = NOTE_SEMITONES[pc];
89
+ if (semitone === undefined)
90
+ continue;
91
+ if (prevSemitone !== -1 && semitone <= prevSemitone) {
92
+ currentOctave++;
93
+ }
94
+ prevSemitone = semitone;
95
+ assigned.push({ pc, kpc, semitone, octave: currentOctave });
96
+ }
97
+ // Step 2: compact — fold notes down if span exceeds playable range
98
+ if (assigned.length > 1) {
99
+ const baseSemi = assigned[0].semitone + assigned[0].octave * 12;
100
+ for (let i = 1; i < assigned.length; i++) {
101
+ const noteSemi = assigned[i].semitone + assigned[i].octave * 12;
102
+ if (noteSemi - baseSemi > MAX_SPAN && assigned[i].octave > assigned[0].octave) {
103
+ assigned[i].octave--;
104
+ }
105
+ }
106
+ }
107
+ for (const a of assigned) {
108
+ midiNoteNames.push(`${a.pc}${a.octave}`);
109
+ const relOctave = a.octave - baseOctave;
110
+ highlightKeys.push(`${a.kpc}:${relOctave}`);
111
+ }
112
+ // 6. Map highlight fills
113
+ const fills = mapHighlights(keys, highlightKeys);
114
+ // 7. Build result
115
+ const result = {
116
+ success: true,
117
+ chordName: parsed.chordName,
118
+ root: resolved.root,
119
+ notes: midiNoteNames,
120
+ inversion: parsed.inversion,
121
+ keyboard: {
122
+ startFrom: layout.startFrom,
123
+ size: layout.size,
124
+ highlightKeys,
125
+ },
126
+ telemetry: {
127
+ parser: "chord",
128
+ resolver: {
129
+ root: resolved.root,
130
+ type: resolved.type,
131
+ pitchClasses,
132
+ },
133
+ durationMs: performance.now() - start,
134
+ },
135
+ };
136
+ // 8. SVG output
137
+ if (outputFormat === "svg") {
138
+ const dims = computeSvgDimensions(layout.size, layoutFormat);
139
+ result.svg = buildSvgString(keys, fills, dims.width, dims.height);
140
+ }
141
+ // 9. MIDI output
142
+ if (outputFormat === "midi") {
143
+ result.midi = generateMidiFile({ notes: pitchClasses });
144
+ }
145
+ // 10. Optional logging
146
+ if (logConfig) {
147
+ const logEntry = {
148
+ input,
149
+ pipeline: {
150
+ parser: "chord",
151
+ parsed: {
152
+ chordName: parsed.chordName,
153
+ inversion: parsed.inversion,
154
+ allInversions: parsed.allInversions,
155
+ startingNote: parsed.startingNote,
156
+ startingDegree: parsed.startingDegree,
157
+ bassNote: parsed.bassNote,
158
+ bassDegree: parsed.bassDegree,
159
+ spanFrom: parsed.spanFrom,
160
+ spanTo: parsed.spanTo,
161
+ padding: parsed.padding,
162
+ chordOctaveShift: parsed.chordOctaveShift,
163
+ bassOctaveShift: parsed.bassOctaveShift,
164
+ styleHint: parsed.styleHint,
165
+ format: parsed.format,
166
+ showNoteNames: parsed.showNoteNames,
167
+ fingering: parsed.fingering,
168
+ autoFingering: parsed.autoFingering,
169
+ },
170
+ resolver: {
171
+ method: "tonal",
172
+ type: resolved.type,
173
+ root: resolved.root,
174
+ pitchClasses,
175
+ },
176
+ voicing: {
177
+ attempted: false,
178
+ found: false,
179
+ },
180
+ layout: {
181
+ startFrom: layout.startFrom,
182
+ size: layout.size,
183
+ chordOctave: layout.chordOctave,
184
+ },
185
+ output: {
186
+ notes: midiNoteNames,
187
+ highlightKeys,
188
+ format: outputFormat,
189
+ },
190
+ },
191
+ success: true,
192
+ durationMs: performance.now() - start,
193
+ };
194
+ logChordRequest(logEntry, logConfig);
195
+ }
196
+ return result;
197
+ }
198
+ catch (err) {
199
+ const msg = err instanceof Error ? err.message : String(err);
200
+ return errorResult(msg);
201
+ }
202
+ }
203
+ //# sourceMappingURL=pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface FormTemplate {
2
+ id: string;
3
+ name: string;
4
+ numerals: string[];
5
+ description: string;
6
+ }
7
+ export declare const FORM_TEMPLATES: FormTemplate[];
8
+ /**
9
+ * Find a form template by ID, name, or keyword alias.
10
+ */
11
+ export declare function findTemplate(query: string): FormTemplate | undefined;
@@ -0,0 +1,105 @@
1
+ export const FORM_TEMPLATES = [
2
+ {
3
+ id: "ii-V-I",
4
+ name: "ii-V-I",
5
+ numerals: ["ii7", "V7", "Imaj7"],
6
+ description: "The most common jazz cadence",
7
+ },
8
+ {
9
+ id: "minor-ii-V-i",
10
+ name: "Minor ii-V-i",
11
+ numerals: ["iiø7", "V7", "i7"],
12
+ description: "Minor key ii-V-i cadence",
13
+ },
14
+ {
15
+ id: "I-vi-ii-V",
16
+ name: "I-vi-ii-V",
17
+ numerals: ["Imaj7", "vi7", "ii7", "V7"],
18
+ description: "Classic turnaround (rhythm changes A section)",
19
+ },
20
+ {
21
+ id: "iii-vi-ii-V",
22
+ name: "iii-vi-ii-V",
23
+ numerals: ["iii7", "vi7", "ii7", "V7"],
24
+ description: "Extended turnaround from the iii chord",
25
+ },
26
+ {
27
+ id: "blues",
28
+ name: "12-Bar Blues",
29
+ numerals: [
30
+ "I7", "IV7", "I7", "I7",
31
+ "IV7", "IV7", "I7", "I7",
32
+ "V7", "IV7", "I7", "V7",
33
+ ],
34
+ description: "Standard 12-bar blues progression",
35
+ },
36
+ {
37
+ id: "jazz-blues",
38
+ name: "Jazz Blues",
39
+ numerals: [
40
+ "I7", "IV7", "I7", "vi7",
41
+ "ii7", "V7", "I7", "vi7",
42
+ "ii7", "V7", "I7", "ii7", "V7",
43
+ ],
44
+ description: "Jazz blues with ii-V turnarounds",
45
+ },
46
+ {
47
+ id: "rhythm-changes",
48
+ name: "Rhythm Changes (A)",
49
+ numerals: ["Imaj7", "vi7", "ii7", "V7", "iii7", "vi7", "ii7", "V7"],
50
+ description: "A section of rhythm changes (I Got Rhythm)",
51
+ },
52
+ {
53
+ id: "modal-vamp",
54
+ name: "Modal Vamp",
55
+ numerals: ["i7", "bVII7"],
56
+ description: "Two-chord modal vamp (So What / Impressions style)",
57
+ },
58
+ {
59
+ id: "backdoor-ii-V",
60
+ name: "Backdoor ii-V",
61
+ numerals: ["iv7", "bVII7", "Imaj7"],
62
+ description: "Backdoor resolution via bVII to I",
63
+ },
64
+ {
65
+ id: "tritone-sub",
66
+ name: "Tritone Sub ii-V-I",
67
+ numerals: ["ii7", "bII7", "Imaj7"],
68
+ description: "ii-V-I with tritone substitution on the V",
69
+ },
70
+ ];
71
+ // Keyword aliases for template lookup
72
+ const TEMPLATE_ALIASES = {
73
+ "blues": "blues",
74
+ "jazzblues": "jazz-blues",
75
+ "rhythmchanges": "rhythm-changes",
76
+ "rhythmchange": "rhythm-changes",
77
+ "turnaround": "I-vi-ii-V",
78
+ "modalvamp": "modal-vamp",
79
+ "backdoor": "backdoor-ii-V",
80
+ "tritonesub": "tritone-sub",
81
+ "tritonesubstitution": "tritone-sub",
82
+ "minoriivi": "minor-ii-V-i",
83
+ "minorii-v-i": "minor-ii-V-i",
84
+ };
85
+ /**
86
+ * Find a form template by ID, name, or keyword alias.
87
+ */
88
+ export function findTemplate(query) {
89
+ const lower = query.toLowerCase().replace(/[\s\-–—]+/g, "");
90
+ // Exact ID match
91
+ const byId = FORM_TEMPLATES.find((t) => t.id.replace(/-/g, "").toLowerCase() === lower);
92
+ if (byId)
93
+ return byId;
94
+ // Alias match
95
+ const aliasId = TEMPLATE_ALIASES[lower];
96
+ if (aliasId) {
97
+ return FORM_TEMPLATES.find((t) => t.id === aliasId);
98
+ }
99
+ // Fuzzy name match
100
+ return FORM_TEMPLATES.find((t) => {
101
+ const name = t.name.toLowerCase().replace(/[\s\-–—]+/g, "");
102
+ return name.includes(lower) || lower.includes(name);
103
+ });
104
+ }
105
+ //# sourceMappingURL=form-templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-templates.js","sourceRoot":"","sources":["../../src/progression/form-templates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { resolveProgression, tokenizeProgression } from "./roman-numeral";
2
+ export { FORM_TEMPLATES, findTemplate } from "./form-templates";
3
+ export type { FormTemplate } from "./form-templates";
4
+ export { resolveProgressionRequest, } from "./progression-resolver";
5
+ export type { ProgressionRequest, ProgressionResult, ProgressionExample, ProgressionChord, } from "./progression-resolver";
@@ -0,0 +1,4 @@
1
+ export { resolveProgression, tokenizeProgression } from "./roman-numeral";
2
+ export { FORM_TEMPLATES, findTemplate } from "./form-templates";
3
+ export { resolveProgressionRequest, } from "./progression-resolver";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/progression/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhE,OAAO,EACL,yBAAyB,GAC1B,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,29 @@
1
+ export interface ProgressionChord {
2
+ symbol: string;
3
+ root: string;
4
+ notes: string[];
5
+ voicingStyle?: string;
6
+ }
7
+ export interface ProgressionExample {
8
+ label: string;
9
+ chords: ProgressionChord[];
10
+ }
11
+ export interface ProgressionResult {
12
+ progressionName: string;
13
+ key: string;
14
+ examples: ProgressionExample[];
15
+ }
16
+ export interface ProgressionRequest {
17
+ /** Roman numeral tokens or a form template name */
18
+ progression: string;
19
+ /** Musical key, e.g. "G", "Db" */
20
+ key: string;
21
+ /** Number of examples to generate */
22
+ numExamples?: number;
23
+ /** Style hint: artist name or voicing style keyword */
24
+ styleHint?: string;
25
+ }
26
+ /**
27
+ * Resolve a progression request into multiple voicing examples.
28
+ */
29
+ export declare function resolveProgressionRequest(req: ProgressionRequest): ProgressionResult;