-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdebug-patterns
More file actions
executable file
·146 lines (121 loc) · 4.3 KB
/
Copy pathdebug-patterns
File metadata and controls
executable file
·146 lines (121 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/env node
import { Scale } from "tonal";
import { diatonicPatterns, pentatonicPatterns } from "../src/frets/system/patterns.js";
/**
* Build a mapping from diatonic scale degree (1-7) to array index
* For 7-note scales: degree 1 -> 0, degree 2 -> 1, etc.
* For 5-note scales: uses intervals to map (e.g., major pent 1,2,3,5,6 -> 0,1,2,3,4)
*/
function buildDegreeToIndexMap(intervals) {
const map = {};
intervals.forEach((interval, index) => {
// Extract the degree number from interval like "1P", "2M", "3m", "5P", "6M"
const degree = parseInt(interval);
map[degree] = index;
});
return map;
}
/**
* Debug utility to visualize pattern indexes as actual note names or intervals
*/
function debugPattern(scaleName, shape, useIntervals = false) {
const scale = Scale.get(scaleName);
const notes = scale.notes;
const intervals = scale.intervals;
if (!scale || !notes || notes.length === 0) {
throw new Error(`Invalid scale: ${scaleName}`);
}
const isPentatonic = notes.length === 5;
const isDiatonic = notes.length === 7;
if (!isPentatonic && !isDiatonic) {
throw new Error(`Scale must be 5 or 7 notes. ${scaleName} has ${notes.length} notes.`);
}
const patterns = isPentatonic ? pentatonicPatterns : diatonicPatterns;
const pattern = patterns[shape];
if (!pattern) {
throw new Error(`Invalid shape: ${shape}. Must be one of C, A, G, E, D`);
}
const stringNames = ["High E", "B", "G", "D", "A", "Low E"];
const displayMode = useIntervals ? "Intervals" : "Notes";
const displayValues = useIntervals ? intervals : notes;
// Build degree-to-index mapping for pentatonic scales
const degreeToIndex = isPentatonic ? buildDegreeToIndexMap(intervals) : null;
console.log(`\n${"=".repeat(60)}`);
console.log(`Scale: ${scaleName}`);
console.log(`Shape: ${shape}`);
console.log(`${displayMode}: ${displayValues.join(", ")}`);
if (isPentatonic) {
console.log(`Degree mapping: ${Object.entries(degreeToIndex).map(([d, i]) => `${d}→${i}`).join(", ")}`);
}
console.log(`${"=".repeat(60)}\n`);
pattern.forEach((stringPattern, stringIndex) => {
const stringName = stringNames[stringIndex].padEnd(6);
const indexes = stringPattern.map(i => String(i).padStart(2)).join(", ");
// For diatonic: degree - 1 gives array index
// For pentatonic: use the degree-to-index mapping
const actualValues = stringPattern.map(degree => {
const index = isPentatonic ? degreeToIndex[degree] : degree - 1;
return displayValues[index].padEnd(3);
}).join(", ");
console.log(`${stringName}: [${indexes}] → [${actualValues}]`);
});
console.log(`\n${"=".repeat(60)}\n`);
}
/**
* Debug all shapes for a given scale
*/
function debugAllShapes(scaleName, useIntervals = false) {
const shapes = ["C", "A", "G", "E", "D"];
shapes.forEach(shape => debugPattern(scaleName, shape, useIntervals));
}
function printUsage() {
console.log(`
Usage: debug-patterns [options]
Options:
-s, --scale <name> Scale name (e.g., "C major", "A minor pentatonic")
-p, --position <shape> CAGED position/shape (C, A, G, E, or D)
-i, --intervals Display intervals instead of note names
-h, --help Show this help message
Examples:
debug-patterns -s "C major" -p G
debug-patterns --scale "A minor pentatonic" --position E
debug-patterns -s "C major" -i
debug-patterns -s "C major" -p G --intervals
If no position is specified, all positions will be displayed.
`);
}
// Parse command line arguments
const args = process.argv.slice(2);
let scaleName = null;
let shape = null;
let useIntervals = false;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === '-h' || arg === '--help') {
printUsage();
process.exit(0);
} else if (arg === '-s' || arg === '--scale') {
scaleName = args[++i];
} else if (arg === '-p' || arg === '--position') {
shape = args[++i];
} else if (arg === '-i' || arg === '--intervals') {
useIntervals = true;
}
}
// Validate required arguments
if (!scaleName) {
console.error('Error: Scale name is required\n');
printUsage();
process.exit(1);
}
// Run the debug
try {
if (shape) {
debugPattern(scaleName, shape, useIntervals);
} else {
debugAllShapes(scaleName, useIntervals);
}
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}