feat: merge mode_list.json into functional_chords.json
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
"ionian": {
|
"ionian": {
|
||||||
|
"name": "Ionian",
|
||||||
|
"steps": [2, 2, 1, 2, 2, 2, 1],
|
||||||
"T": ["I", "vi", "iii", "I⁶"],
|
"T": ["I", "vi", "iii", "I⁶"],
|
||||||
"S": ["IV", "ii", "vi", "IV⁶"],
|
"S": ["IV", "ii", "vi", "IV⁶"],
|
||||||
"D": ["V", "V7", "vii°", "♭II"],
|
"D": ["V", "V7", "vii°", "♭II"],
|
||||||
@@ -18,6 +20,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"aeolian": {
|
"aeolian": {
|
||||||
|
"name": "Aeolian",
|
||||||
|
"steps": [2, 1, 2, 2, 1, 2, 2],
|
||||||
"T": ["i", "VI", "III", "i⁶"],
|
"T": ["i", "VI", "III", "i⁶"],
|
||||||
"S": ["iv", "ii°", "VI", "iv⁶"],
|
"S": ["iv", "ii°", "VI", "iv⁶"],
|
||||||
"D": ["v", "♭VII"],
|
"D": ["v", "♭VII"],
|
||||||
@@ -34,6 +38,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"harmonic_minor": {
|
"harmonic_minor": {
|
||||||
|
"name": "Harmonic Minor",
|
||||||
|
"steps": [2, 1, 2, 2, 1, 3, 1],
|
||||||
"T": ["i", "VI", "III+", "i⁶"],
|
"T": ["i", "VI", "III+", "i⁶"],
|
||||||
"S": ["iv", "ii°", "VI", "iv⁶"],
|
"S": ["iv", "ii°", "VI", "iv⁶"],
|
||||||
"D": ["V", "V7", "vii°", "♭II"],
|
"D": ["V", "V7", "vii°", "♭II"],
|
||||||
@@ -52,6 +58,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"melodic_minor": {
|
"melodic_minor": {
|
||||||
|
"name": "Melodic Minor",
|
||||||
|
"steps": [2, 1, 2, 2, 2, 2, 1],
|
||||||
"T": ["i", "III+", "vi", "i⁶"],
|
"T": ["i", "III+", "vi", "i⁶"],
|
||||||
"S": ["IV", "ii"],
|
"S": ["IV", "ii"],
|
||||||
"D": ["V", "vii°"],
|
"D": ["V", "vii°"],
|
||||||
@@ -67,6 +75,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dorian": {
|
"dorian": {
|
||||||
|
"name": "Dorian",
|
||||||
|
"steps": [2, 1, 2, 2, 2, 1, 2],
|
||||||
"T": ["i", "III", "i⁶"],
|
"T": ["i", "III", "i⁶"],
|
||||||
"S": ["IV", "ii"],
|
"S": ["IV", "ii"],
|
||||||
"D": ["v", "♭VII"],
|
"D": ["v", "♭VII"],
|
||||||
@@ -81,6 +91,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"phrygian": {
|
"phrygian": {
|
||||||
|
"name": "Phrygian",
|
||||||
|
"steps": [1, 2, 2, 2, 1, 2, 2],
|
||||||
"T": ["i", "i⁶"],
|
"T": ["i", "i⁶"],
|
||||||
"S": ["♭II", "iv"],
|
"S": ["♭II", "iv"],
|
||||||
"D": [],
|
"D": [],
|
||||||
@@ -92,6 +104,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lydian": {
|
"lydian": {
|
||||||
|
"name": "Lydian",
|
||||||
|
"steps": [2, 2, 2, 1, 2, 2, 1],
|
||||||
"T": ["I", "I⁶"],
|
"T": ["I", "I⁶"],
|
||||||
"S": ["II"],
|
"S": ["II"],
|
||||||
"D": ["V"],
|
"D": ["V"],
|
||||||
@@ -103,6 +117,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mixolydian": {
|
"mixolydian": {
|
||||||
|
"name": "Mixolydian",
|
||||||
|
"steps": [2, 2, 1, 2, 2, 1, 2],
|
||||||
"T": ["I", "vi"],
|
"T": ["I", "vi"],
|
||||||
"S": ["IV", "ii"],
|
"S": ["IV", "ii"],
|
||||||
"D": ["v", "♭VII"],
|
"D": ["v", "♭VII"],
|
||||||
@@ -116,6 +132,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"locrian": {
|
"locrian": {
|
||||||
|
"name": "Locrian",
|
||||||
|
"steps": [1, 2, 2, 1, 2, 2, 2],
|
||||||
"T": ["i°"],
|
"T": ["i°"],
|
||||||
"S": ["♭II", "iv"],
|
"S": ["♭II", "iv"],
|
||||||
"D": [],
|
"D": [],
|
||||||
@@ -126,6 +144,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"phrygian_dominant": {
|
"phrygian_dominant": {
|
||||||
|
"name": "Phrygian Dominant",
|
||||||
|
"steps": [1, 3, 1, 2, 1, 2, 2],
|
||||||
"T": ["I", "I⁶"],
|
"T": ["I", "I⁶"],
|
||||||
"S": ["♭II", "iv"],
|
"S": ["♭II", "iv"],
|
||||||
"D": ["v°", "vii"],
|
"D": ["v°", "vii"],
|
||||||
@@ -139,6 +159,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"harmonic_major": {
|
"harmonic_major": {
|
||||||
|
"name": "Harmonic Major",
|
||||||
|
"steps": [2, 2, 1, 2, 1, 3, 1],
|
||||||
"T": ["I", "VI", "iii"],
|
"T": ["I", "VI", "iii"],
|
||||||
"S": ["IV", "ii"],
|
"S": ["IV", "ii"],
|
||||||
"D": ["V", "V7", "vii°", "♭II"],
|
"D": ["V", "V7", "vii°", "♭II"],
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
{
|
|
||||||
"modes": [
|
|
||||||
{
|
|
||||||
"id": "ionian",
|
|
||||||
"name": "Ionian",
|
|
||||||
"steps": [2, 2, 1, 2, 2, 2, 1]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "aeolian",
|
|
||||||
"name": "Aeolian",
|
|
||||||
"steps": [2, 1, 2, 2, 1, 2, 2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "harmonic_minor",
|
|
||||||
"name": "Harmonic Minor",
|
|
||||||
"steps": [2, 1, 2, 2, 1, 3, 1]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "melodic_minor",
|
|
||||||
"name": "Melodic Minor",
|
|
||||||
"steps": [2, 1, 2, 2, 2, 2, 1]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "dorian",
|
|
||||||
"name": "Dorian",
|
|
||||||
"steps": [2, 1, 2, 2, 2, 1, 2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "phrygian",
|
|
||||||
"name": "Phrygian",
|
|
||||||
"steps": [1, 2, 2, 2, 1, 2, 2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "lydian",
|
|
||||||
"name": "Lydian",
|
|
||||||
"steps": [2, 2, 2, 1, 2, 2, 1]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mixolydian",
|
|
||||||
"name": "Mixolydian",
|
|
||||||
"steps": [2, 2, 1, 2, 2, 1, 2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "locrian",
|
|
||||||
"name": "Locrian",
|
|
||||||
"steps": [1, 2, 2, 1, 2, 2, 2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "phrygian_dominant",
|
|
||||||
"name": "Phrygian Dominant",
|
|
||||||
"steps": [1, 3, 1, 2, 1, 2, 2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "harmonic_major",
|
|
||||||
"name": "Harmonic Major",
|
|
||||||
"steps": [2, 2, 1, 2, 1, 3, 1]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
+13
-11
@@ -45,28 +45,30 @@ function App() {
|
|||||||
|
|
||||||
// Load all mode and chord data files in parallel
|
// Load all mode and chord data files in parallel
|
||||||
try {
|
try {
|
||||||
const [modeListResponse, functionalChordsResponse] = await Promise.all([
|
const [functionalChordsResponse] = await Promise.all([
|
||||||
fetch(`${import.meta.env.BASE_URL}resources/modes/mode_list.json`),
|
|
||||||
fetch(`${import.meta.env.BASE_URL}resources/modes/functional_chords.json`)
|
fetch(`${import.meta.env.BASE_URL}resources/modes/functional_chords.json`)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const [modeListData, functionalChordsData] = await Promise.all([
|
const [functionalChordsData] = await Promise.all([
|
||||||
modeListResponse.json(),
|
|
||||||
functionalChordsResponse.json()
|
functionalChordsResponse.json()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Store mode data with id, name, and steps
|
// Store functional chords data (includes name, steps, T/S/D groups, and chord notes for each mode)
|
||||||
KGCore.MODE_DATA = modeListData.modes;
|
|
||||||
console.log(`Loaded ${modeListData.modes.length} modes:`, modeListData.modes.map((m: { name: string }) => m.name));
|
|
||||||
|
|
||||||
// Store functional chords data (T/S/D by mode, including mode-specific chord notes)
|
|
||||||
KGCore.FUNCTIONAL_CHORDS_DATA = functionalChordsData;
|
KGCore.FUNCTIONAL_CHORDS_DATA = functionalChordsData;
|
||||||
console.log(`Loaded functional chords for ${Object.keys(functionalChordsData).length} modes`);
|
console.log(`Loaded functional chords for ${Object.keys(functionalChordsData).length} modes`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load mode/chord data:', error);
|
console.error('Failed to load mode/chord data:', error);
|
||||||
// Fallback to defaults
|
// Fallback to defaults
|
||||||
KGCore.MODE_DATA = [{ id: 'ionian', name: 'Ionian', steps: [2, 2, 1, 2, 2, 2, 1] }];
|
KGCore.FUNCTIONAL_CHORDS_DATA = {
|
||||||
KGCore.FUNCTIONAL_CHORDS_DATA = {};
|
ionian: {
|
||||||
|
name: 'Ionian',
|
||||||
|
steps: [2, 2, 1, 2, 2, 2, 1],
|
||||||
|
T: [],
|
||||||
|
S: [],
|
||||||
|
D: [],
|
||||||
|
chords: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log maxBars after initialization completes
|
// Log maxBars after initialization completes
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const PianoRollToolbar: React.FC<PianoRollToolbarProps> = ({
|
|||||||
<div className="toolbar-left">
|
<div className="toolbar-left">
|
||||||
{/* Left section with mode and chord guide dropdowns */}
|
{/* Left section with mode and chord guide dropdowns */}
|
||||||
<KGDropdown
|
<KGDropdown
|
||||||
options={KGCore.MODE_DATA.map(m => ({ label: m.name, value: m.id }))}
|
options={Object.entries(KGCore.FUNCTIONAL_CHORDS_DATA).map(([id, data]) => ({ label: data.name, value: id }))}
|
||||||
value={selectedMode}
|
value={selectedMode}
|
||||||
onChange={(value) => onModeChange(value)}
|
onChange={(value) => onModeChange(value)}
|
||||||
label="Mode"
|
label="Mode"
|
||||||
|
|||||||
+1
-2
@@ -18,8 +18,7 @@ export class KGCore {
|
|||||||
private static _instance: KGCore | null = null;
|
private static _instance: KGCore | null = null;
|
||||||
|
|
||||||
// Global music data resources
|
// Global music data resources
|
||||||
public static MODE_DATA: Array<{ id: string; name: string; steps: number[] }> = []; // Modes with id, display name, and interval steps
|
public static FUNCTIONAL_CHORDS_DATA: Record<string, { name: string; steps: number[]; T: string[]; S: string[]; D: string[]; chords: Record<string, string[]> }> = {}; // Functional chords by mode (T/S/D) with mode-specific chord notes, and mode metadata
|
||||||
public static FUNCTIONAL_CHORDS_DATA: Record<string, { T: string[]; S: string[]; D: string[]; chords: Record<string, string[]> }> = {}; // Functional chords by mode (T/S/D) with mode-specific chord notes
|
|
||||||
|
|
||||||
private currentProject: KGProject = new KGProject();
|
private currentProject: KGProject = new KGProject();
|
||||||
|
|
||||||
|
|||||||
@@ -16,15 +16,7 @@ import functionalChordsData from '../../public/resources/modes/functional_chords
|
|||||||
describe('scaleUtil', () => {
|
describe('scaleUtil', () => {
|
||||||
// Setup: Mock KGCore with real chord data
|
// Setup: Mock KGCore with real chord data
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Mock MODE_DATA with ionian for basic tests
|
// Use real functional chords data from JSON file (includes name, steps, and chord data)
|
||||||
KGCore.MODE_DATA = [
|
|
||||||
{ id: 'ionian', name: 'Ionian', steps: [2, 2, 1, 2, 2, 2, 1] },
|
|
||||||
{ id: 'dorian', name: 'Dorian', steps: [2, 1, 2, 2, 2, 1, 2] },
|
|
||||||
{ id: 'aeolian', name: 'Aeolian', steps: [2, 1, 2, 2, 1, 2, 2] },
|
|
||||||
{ id: 'mixolydian', name: 'Mixolydian', steps: [2, 2, 1, 2, 2, 1, 2] }
|
|
||||||
]
|
|
||||||
|
|
||||||
// Use real functional chords data from JSON file
|
|
||||||
KGCore.FUNCTIONAL_CHORDS_DATA = functionalChordsData
|
KGCore.FUNCTIONAL_CHORDS_DATA = functionalChordsData
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -66,12 +66,12 @@ export const getScalePitchClasses = (rootNote: string, modeSteps: number[]): num
|
|||||||
* @returns Array of interval steps, or default ionian if not found
|
* @returns Array of interval steps, or default ionian if not found
|
||||||
*/
|
*/
|
||||||
export const getModeSteps = (modeId: string): number[] => {
|
export const getModeSteps = (modeId: string): number[] => {
|
||||||
const modeData = KGCore.MODE_DATA.find(m => m.id === modeId);
|
const functionalChords = KGCore.FUNCTIONAL_CHORDS_DATA[modeId];
|
||||||
if (!modeData) {
|
if (!functionalChords || !functionalChords.steps) {
|
||||||
console.warn(`Mode not found: ${modeId}, defaulting to ionian`);
|
console.warn(`Mode not found: ${modeId}, defaulting to ionian`);
|
||||||
return [2, 2, 1, 2, 2, 2, 1]; // Default to ionian (major scale)
|
return [2, 2, 1, 2, 2, 2, 1]; // Default to ionian (major scale)
|
||||||
}
|
}
|
||||||
return modeData.steps;
|
return functionalChords.steps;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user