@medyll/idae-cadenzia 0.1.0 → 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.
- package/CHANGELOG.md +15 -0
- package/package.json +1 -1
- package/src/lib/components/ChordTable.svelte +80 -12
- package/src/lib/components/ChordVisualization.svelte +15 -14
- package/src/lib/constants/constants.ts +49 -0
- package/src/lib/functions/functions.svelte.ts +72 -22
- package/src/lib/functions/rules.ts +118 -0
- package/src/lib/index.ts +2 -0
- package/src/lib/types/types.ts +8 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @medyll/idae-cadenzia
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- - feat(ajouter): la gestion des informations de mesure pour les entrées d'accords ([b0641d0](https://github.com/medyll/idae/commit/b0641d0e4c236717555b6cb2a853f127092ee194)) - 2024-11-18 by @medyll
|
|
8
|
+
- feat(ajouter): des constantes musicales et améliorer la gestion des accords avec des options d'armure ([0f48cd9](https://github.com/medyll/idae/commit/0f48cd947ceabadf13a7488110c8bbdc6b32e638)) - 2024-11-16 by @medyll
|
|
9
|
+
- feat(ajouter): le composant App et les composants associés pour la visualisation des accords ([e7c9517](https://github.com/medyll/idae/commit/e7c95178e328e56f514678bed4c96015e7ef7529)) - 2024-11-16 by @medyll
|
|
10
|
+
|
|
11
|
+
## 0.2.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- - feat(ajouter): des constantes musicales et améliorer la gestion des accords avec des options d'armure ([0f48cd9](https://github.com/medyll/idae/commit/0f48cd947ceabadf13a7488110c8bbdc6b32e638)) - 2024-11-16 by @medyll
|
|
16
|
+
- feat(ajouter): le composant App et les composants associés pour la visualisation des accords ([e7c9517](https://github.com/medyll/idae/commit/e7c95178e328e56f514678bed4c96015e7ef7529)) - 2024-11-16 by @medyll
|
|
17
|
+
|
|
3
18
|
## 0.1.0
|
|
4
19
|
|
|
5
20
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
<!-- components/ChordTable.svelte -->
|
|
2
2
|
<script lang="ts">
|
|
3
3
|
import {
|
|
4
|
-
chordEntries,
|
|
5
|
-
updateCadences,
|
|
6
4
|
qualities,
|
|
5
|
+
armorOptions,
|
|
6
|
+
modes,
|
|
7
7
|
rootNotes,
|
|
8
|
-
modifiers
|
|
9
|
-
|
|
10
|
-
} from '
|
|
8
|
+
modifiers
|
|
9
|
+
} from '$lib/constants/constants.js';
|
|
10
|
+
import { getScaleNotes, isChordInScale } from '$lib/functions/rules.js';
|
|
11
|
+
|
|
12
|
+
import type { ChordEntry } from '$lib/types/types.js';
|
|
13
|
+
import { chordEntries, updateCadences, toggleModifier } from '../functions/functions.svelte.js';
|
|
11
14
|
|
|
12
15
|
function addChordEntry() {
|
|
13
16
|
chordEntries.push({
|
|
14
17
|
chord: { root: 'C', quality: qualities.mode[0], modifier: undefined, duration: '1' },
|
|
15
|
-
timeSignature: chordEntries.length === 0 ? { numerator: 4, denominator: 4 } : undefined
|
|
18
|
+
timeSignature: chordEntries.length === 0 ? { numerator: 4, denominator: 4 } : undefined,
|
|
19
|
+
armor: '',
|
|
20
|
+
mode: undefined,
|
|
21
|
+
measureInfo: { start: 1, end: 1, beatStart: 0 }
|
|
16
22
|
});
|
|
17
23
|
updateCadences();
|
|
18
24
|
}
|
|
@@ -33,6 +39,15 @@
|
|
|
33
39
|
chordEntries[index].timeSignature = { numerator: 4, denominator: 4 };
|
|
34
40
|
}
|
|
35
41
|
}
|
|
42
|
+
|
|
43
|
+
function isNoteInScale(note: string, entry: ChordEntry): boolean {
|
|
44
|
+
const scaleNotes = getScaleNotes(entry.armor);
|
|
45
|
+
return scaleNotes.includes(note);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isChordValid(entry: ChordEntry): boolean {
|
|
49
|
+
return isChordInScale(entry);
|
|
50
|
+
}
|
|
36
51
|
</script>
|
|
37
52
|
|
|
38
53
|
<table>
|
|
@@ -40,6 +55,7 @@
|
|
|
40
55
|
<tr>
|
|
41
56
|
<th>Measure</th>
|
|
42
57
|
<th>Time Signature</th>
|
|
58
|
+
<th>Armor</th>
|
|
43
59
|
<th>Root</th>
|
|
44
60
|
<th>Quality</th>
|
|
45
61
|
<th>Modifier</th>
|
|
@@ -51,7 +67,7 @@
|
|
|
51
67
|
{#each chordEntries as entry, i}
|
|
52
68
|
<tr>
|
|
53
69
|
<td>{i + 1}</td>
|
|
54
|
-
<td>
|
|
70
|
+
<td class="timeSignature">
|
|
55
71
|
{#if entry.timeSignature}
|
|
56
72
|
<input type="number" bind:value={entry.timeSignature.numerator} min="1" max="32" />
|
|
57
73
|
/
|
|
@@ -60,10 +76,34 @@
|
|
|
60
76
|
<button onclick={() => toggleTimeSignature(i)}>Add Time Signature</button>
|
|
61
77
|
{/if}
|
|
62
78
|
</td>
|
|
79
|
+
<td>
|
|
80
|
+
<div>
|
|
81
|
+
<label for="armor-{i}">Armor:</label>
|
|
82
|
+
<select id="armor-{i}" bind:value={entry.armor} onchange={handleChordChange}>
|
|
83
|
+
{#each armorOptions as armor}
|
|
84
|
+
<option value={armor.name}>
|
|
85
|
+
{armor.name}
|
|
86
|
+
{armor.value ? `(${armor.value})` : ''}
|
|
87
|
+
</option>
|
|
88
|
+
{/each}
|
|
89
|
+
</select>
|
|
90
|
+
</div>
|
|
91
|
+
<div>
|
|
92
|
+
<label for="mode-{i}">Mode:</label>
|
|
93
|
+
<select id="mode-{i}" bind:value={entry.mode} onchange={handleChordChange}>
|
|
94
|
+
<option value={undefined}>Select mode</option>
|
|
95
|
+
{#each modes as mode}
|
|
96
|
+
<option value={mode}>{mode}</option>
|
|
97
|
+
{/each}
|
|
98
|
+
</select>
|
|
99
|
+
</div>
|
|
100
|
+
</td>
|
|
63
101
|
<td>
|
|
64
102
|
<select bind:value={entry.chord.root} onchange={handleChordChange}>
|
|
65
103
|
{#each rootNotes as note}
|
|
66
|
-
<option value={note}
|
|
104
|
+
<option value={note} class:not-in-scale={!isChordValid({...entry, chord: {...entry.chord, root: note}})}>
|
|
105
|
+
{note}
|
|
106
|
+
</option>
|
|
67
107
|
{/each}
|
|
68
108
|
</select>
|
|
69
109
|
</td>
|
|
@@ -71,7 +111,7 @@
|
|
|
71
111
|
{#each Object.entries(qualities) as [group, qualityOptions]}
|
|
72
112
|
<div>
|
|
73
113
|
{#each qualityOptions as quality}
|
|
74
|
-
<label>
|
|
114
|
+
<label class:not-in-scale={!isChordValid({...entry, chord: {...entry.chord, quality}})}>
|
|
75
115
|
<input
|
|
76
116
|
type="radio"
|
|
77
117
|
name={`quality-${group}-${i}`}
|
|
@@ -92,7 +132,7 @@
|
|
|
92
132
|
</td>
|
|
93
133
|
<td>
|
|
94
134
|
{#each modifiers as modifier}
|
|
95
|
-
<label>
|
|
135
|
+
<label class:not-in-scale={!isChordValid({...entry, chord: {...entry.chord, modifier}})}>
|
|
96
136
|
<input
|
|
97
137
|
type="radio"
|
|
98
138
|
name={`modifier-${i}`}
|
|
@@ -104,7 +144,7 @@
|
|
|
104
144
|
</label>
|
|
105
145
|
{/each}
|
|
106
146
|
</td>
|
|
107
|
-
<td>
|
|
147
|
+
<td class="duration">
|
|
108
148
|
<input
|
|
109
149
|
type="text"
|
|
110
150
|
bind:value={entry.chord.duration}
|
|
@@ -132,7 +172,27 @@
|
|
|
132
172
|
td {
|
|
133
173
|
border: 1px solid #ddd;
|
|
134
174
|
padding: 8px;
|
|
135
|
-
text-align:
|
|
175
|
+
text-align: center;
|
|
176
|
+
}
|
|
177
|
+
td.timeSignature {
|
|
178
|
+
& input[type='number'] {
|
|
179
|
+
width: 40px;
|
|
180
|
+
text-align: center;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
& button {
|
|
184
|
+
padding: 5px 10px;
|
|
185
|
+
background-color: #f0f0f0;
|
|
186
|
+
border: 1px solid #ccc;
|
|
187
|
+
border-radius: 3px;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
td.duration {
|
|
192
|
+
& input[type='text'] {
|
|
193
|
+
width: 60px;
|
|
194
|
+
text-align: center;
|
|
195
|
+
}
|
|
136
196
|
}
|
|
137
197
|
th {
|
|
138
198
|
background-color: #f2f2f2;
|
|
@@ -142,4 +202,12 @@
|
|
|
142
202
|
input[type='number'] {
|
|
143
203
|
width: 100%;
|
|
144
204
|
}
|
|
205
|
+
|
|
206
|
+
option.not-in-scale {
|
|
207
|
+
color: red;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
label.not-in-scale {
|
|
211
|
+
color: red;
|
|
212
|
+
}
|
|
145
213
|
</style>
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
<!-- components/ChordVisualization.svelte -->
|
|
2
2
|
<script lang="ts">
|
|
3
|
-
import { chordEntries } from '$lib/functions/functions.svelte';
|
|
3
|
+
import { chordEntries, getArmorInfo } from '$lib/functions/functions.svelte';
|
|
4
4
|
import type { ChordEntry } from '$lib/types/types';
|
|
5
5
|
|
|
6
6
|
function getChordName(entry: ChordEntry) {
|
|
7
7
|
const { chord } = entry;
|
|
8
8
|
let name = chord.root;
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
name += chord.modifier;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
name += chord.mode ?? '';
|
|
10
|
+
name += chord.modifier ?? '';
|
|
15
11
|
name += chord.augDim ?? '';
|
|
16
12
|
name += chord.sus ?? '';
|
|
17
13
|
name += chord.sept ?? '';
|
|
@@ -35,15 +31,20 @@
|
|
|
35
31
|
{:else}
|
|
36
32
|
<div class="chord-sequence">
|
|
37
33
|
{#each chordEntries as entry, i}
|
|
38
|
-
{#if entry.timeSignature}
|
|
34
|
+
{#if entry.timeSignature || entry.armor || entry.mode}
|
|
39
35
|
<div class="signature">
|
|
40
|
-
|
|
36
|
+
{#if entry.timeSignature}
|
|
37
|
+
- Signature: {entry.timeSignature.numerator}/{entry.timeSignature.denominator}
|
|
38
|
+
{/if}
|
|
39
|
+
{#if entry.armor}
|
|
40
|
+
- Armure: {getArmorInfo(entry.armor)}
|
|
41
|
+
{/if}
|
|
42
|
+
{#if entry.mode}
|
|
43
|
+
- Mode: {entry.mode}
|
|
44
|
+
{/if}
|
|
41
45
|
</div>
|
|
42
46
|
{/if}
|
|
43
|
-
<div
|
|
44
|
-
class="chord"
|
|
45
|
-
style="--baseTime:{getDurationValue(entry.chord.duration)}"
|
|
46
|
-
>
|
|
47
|
+
<div class="chord" style="--baseTime:{getDurationValue(entry.chord.duration)}">
|
|
47
48
|
<span>{getChordName(entry)}</span>
|
|
48
49
|
<span>{entry.chord.duration}</span>
|
|
49
50
|
</div>
|
|
@@ -72,10 +73,10 @@
|
|
|
72
73
|
background-color: #fff;
|
|
73
74
|
border: 1px solid #ccc;
|
|
74
75
|
border-radius: 5px;
|
|
75
|
-
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
|
76
|
+
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
|
76
77
|
text-align: center;
|
|
77
78
|
width: calc(200px * var(--baseTime, 1));
|
|
78
79
|
min-width: 30px; /* Ensure a minimum width for very short durations */
|
|
79
80
|
max-width: 100%; /* Prevent oversized chords */
|
|
80
81
|
}
|
|
81
|
-
</style>
|
|
82
|
+
</style>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* constants/constants.ts */
|
|
2
|
+
import type { Cadence } from '$lib/types/types';
|
|
3
|
+
|
|
4
|
+
// Definition of common musical cadences
|
|
5
|
+
export const cadencePatterns: Cadence[] = [
|
|
6
|
+
{ name: 'Perfect', chords: ['V', 'I'] },
|
|
7
|
+
{ name: 'Plagal', chords: ['IV', 'I'] },
|
|
8
|
+
{ name: 'Deceptive', chords: ['V', 'VI'] },
|
|
9
|
+
{ name: 'Half', chords: ['I', 'V'] },
|
|
10
|
+
{ name: 'Italian', chords: ['IV', 'V', 'I'] },
|
|
11
|
+
{ name: 'German', chords: ['II', 'V', 'I'] },
|
|
12
|
+
{ name: 'Phrygian', chords: ['IV', 'V', 'III'] }
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
// Fundamental root notes in order
|
|
16
|
+
export const rootNotes = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
|
|
17
|
+
|
|
18
|
+
// Different chord qualities grouped by category
|
|
19
|
+
export const qualities = {
|
|
20
|
+
mode: ['maj', 'min'],
|
|
21
|
+
augDim: ['aug', 'dim'],
|
|
22
|
+
sus: ['sus4', 'sus2'],
|
|
23
|
+
sept: ['7', '5']
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Modifier symbols (sharp, flat, natural)
|
|
27
|
+
export const modifiers = ['♯', '♭', '♮'];
|
|
28
|
+
|
|
29
|
+
// Armor options with alteration value and key name
|
|
30
|
+
export const armorOptions = [
|
|
31
|
+
{ value: '', name: 'C' },
|
|
32
|
+
{ value: '1♯', name: 'G' },
|
|
33
|
+
{ value: '2♯', name: 'D' },
|
|
34
|
+
{ value: '3♯', name: 'A' },
|
|
35
|
+
{ value: '4♯', name: 'E' },
|
|
36
|
+
{ value: '5♯', name: 'B' },
|
|
37
|
+
{ value: '6♯', name: 'F♯' },
|
|
38
|
+
{ value: '7♯', name: 'C♯' },
|
|
39
|
+
{ value: '1♭', name: 'F' },
|
|
40
|
+
{ value: '2♭', name: 'B♭' },
|
|
41
|
+
{ value: '3♭', name: 'E♭' },
|
|
42
|
+
{ value: '4♭', name: 'A♭' },
|
|
43
|
+
{ value: '5♭', name: 'D♭' },
|
|
44
|
+
{ value: '6♭', name: 'G♭' },
|
|
45
|
+
{ value: '7♭', name: 'C♭' }
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// Musical modes
|
|
49
|
+
export const modes = ['Ionian', 'Dorian', 'Phrygian', 'Lydian', 'Mixolydian', 'Aeolian', 'Locrian'];
|
|
@@ -1,31 +1,11 @@
|
|
|
1
1
|
// functions/functions.svelte.ts
|
|
2
|
-
import
|
|
2
|
+
import { rootNotes, cadencePatterns, armorOptions } from '$lib/constants/constants.js';
|
|
3
|
+
import type { Chord, Cadence, ChordEntry } from '$lib/types/types';
|
|
3
4
|
|
|
4
5
|
export const chords = $state<Chord[]>([]);
|
|
5
6
|
export const suggestedCadences = $state<Cadence[]>([]);
|
|
6
7
|
export const chordEntries = $state<ChordEntry[]>([]);
|
|
7
8
|
|
|
8
|
-
export const cadencePatterns: Cadence[] = [
|
|
9
|
-
{ name: 'Perfect', chords: ['V', 'I'] },
|
|
10
|
-
{ name: 'Plagal', chords: ['IV', 'I'] },
|
|
11
|
-
{ name: 'Deceptive', chords: ['V', 'VI'] },
|
|
12
|
-
{ name: 'Half', chords: ['I', 'V'] },
|
|
13
|
-
{ name: 'Italian', chords: ['IV', 'V', 'I'] },
|
|
14
|
-
{ name: 'German', chords: ['II', 'V', 'I'] },
|
|
15
|
-
{ name: 'Phrygian', chords: ['IV', 'V', 'III'] }
|
|
16
|
-
];
|
|
17
|
-
|
|
18
|
-
export const rootNotes = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
|
|
19
|
-
|
|
20
|
-
export const qualities = {
|
|
21
|
-
mode: ['maj', 'min'],
|
|
22
|
-
augDim: ['aug', 'dim'],
|
|
23
|
-
sus: ['sus4', 'sus2'],
|
|
24
|
-
sept: ['7', '5']
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export const modifiers = ['♯', '♭', '♮'];
|
|
28
|
-
|
|
29
9
|
export function updateCadences() {
|
|
30
10
|
console.log('Current Chords:', $state.snapshot({ chords }));
|
|
31
11
|
|
|
@@ -35,6 +15,9 @@ export function updateCadences() {
|
|
|
35
15
|
return;
|
|
36
16
|
}
|
|
37
17
|
|
|
18
|
+
const lastEntry = chordEntries[chordEntries.length - 1];
|
|
19
|
+
const lastChordMode = lastEntry.mode;
|
|
20
|
+
|
|
38
21
|
const romanNumerals = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII'];
|
|
39
22
|
const lastChord = chordEntries[chordEntries.length - 1].chord;
|
|
40
23
|
|
|
@@ -64,3 +47,70 @@ export function toggleModifier(chordIndex: number, modifier: string) {
|
|
|
64
47
|
chord.modifier = chord.modifier === modifier ? undefined : modifier;
|
|
65
48
|
updateCadences();
|
|
66
49
|
}
|
|
50
|
+
|
|
51
|
+
export function getArmorInfo(armorName: string) {
|
|
52
|
+
const armor = armorOptions.find((a) => a.name === armorName);
|
|
53
|
+
return armor ? `${armor.name}${armor.value ? ` (${armor.value})` : ''}` : '';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getDurationValue(duration: string): number {
|
|
57
|
+
const [numerator, denominator] = duration.split('/').map(Number);
|
|
58
|
+
return denominator ? numerator / denominator : numerator;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function updateMeasureInfo() {
|
|
62
|
+
let currentMeasure = 1;
|
|
63
|
+
let currentBeat = 0;
|
|
64
|
+
let currentTimeSignature = { numerator: 4, denominator: 4 };
|
|
65
|
+
|
|
66
|
+
for (let i = 0; i < chordEntries.length; i++) {
|
|
67
|
+
const entry = chordEntries[i];
|
|
68
|
+
|
|
69
|
+
if (entry.timeSignature) {
|
|
70
|
+
currentTimeSignature = entry.timeSignature;
|
|
71
|
+
if (currentBeat > 0) {
|
|
72
|
+
currentMeasure++;
|
|
73
|
+
currentBeat = 0;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const chordDuration = getDurationValue(entry.chord.duration);
|
|
78
|
+
const beatsPerMeasure = currentTimeSignature.numerator;
|
|
79
|
+
|
|
80
|
+
const measureStart = currentMeasure;
|
|
81
|
+
const beatStart = currentBeat;
|
|
82
|
+
|
|
83
|
+
currentBeat += chordDuration * beatsPerMeasure;
|
|
84
|
+
while (currentBeat >= beatsPerMeasure) {
|
|
85
|
+
currentMeasure++;
|
|
86
|
+
currentBeat -= beatsPerMeasure;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
chordEntries[i] = {
|
|
90
|
+
...entry,
|
|
91
|
+
measureInfo: {
|
|
92
|
+
start: measureStart,
|
|
93
|
+
end: currentMeasure,
|
|
94
|
+
beatStart: beatStart
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function addChordEntry() {
|
|
101
|
+
const newEntry = {
|
|
102
|
+
chord: { root: 'C', quality: 'maj', modifier: undefined, duration: '1' },
|
|
103
|
+
timeSignature: chordEntries.length === 0 ? { numerator: 4, denominator: 4 } : undefined,
|
|
104
|
+
armor: '',
|
|
105
|
+
measureInfo: { start: 1, end: 1, beatStart: 0 }
|
|
106
|
+
};
|
|
107
|
+
chordEntries.push(newEntry);
|
|
108
|
+
updateCadences();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function updateChordEntry(index: number, updatedEntry: Partial<ChordEntry>) {
|
|
112
|
+
if (index >= 0 && index < chordEntries.length) {
|
|
113
|
+
chordEntries[index] = { ...chordEntries[index], ...updatedEntry };
|
|
114
|
+
updateCadences();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// functions/rules.ts
|
|
2
|
+
import { rootNotes, armorOptions, modes } from '$lib/constants/constants.js';
|
|
3
|
+
import type { ChordEntry } from '../types/types';
|
|
4
|
+
|
|
5
|
+
// Function to get scale notes based on the armor
|
|
6
|
+
export function getScaleNotes(armor: string): string[] {
|
|
7
|
+
const baseScale = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
|
|
8
|
+
const armorInfo = armorOptions.find((a) => a.name === armor);
|
|
9
|
+
|
|
10
|
+
if (!armorInfo) return baseScale;
|
|
11
|
+
|
|
12
|
+
const sharpCount = armorInfo.value.includes('♯') ? parseInt(armorInfo.value) : 0;
|
|
13
|
+
const flatCount = armorInfo.value.includes('♭') ? parseInt(armorInfo.value) : 0;
|
|
14
|
+
|
|
15
|
+
if (sharpCount > 0) {
|
|
16
|
+
const sharps = ['F', 'C', 'G', 'D', 'A', 'E', 'B'].slice(0, sharpCount);
|
|
17
|
+
return baseScale.map((note) => (sharps.includes(note) ? note + '♯' : note));
|
|
18
|
+
} else if (flatCount > 0) {
|
|
19
|
+
const flats = ['B', 'E', 'A', 'D', 'G', 'C', 'F'].slice(0, flatCount);
|
|
20
|
+
return baseScale.map((note) => (flats.includes(note) ? note + '♭' : note));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return baseScale;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Function to check if a chord is in the scale
|
|
27
|
+
export function isChordInScale(chordEntry: ChordEntry): boolean {
|
|
28
|
+
const scaleNotes = getScaleNotes(chordEntry.armor);
|
|
29
|
+
const { root, quality, modifier } = chordEntry.chord;
|
|
30
|
+
console.log({ scaleNotes });
|
|
31
|
+
// Check if the root note is in the scale
|
|
32
|
+
// if (!scaleNotes.includes(root)) return false;
|
|
33
|
+
|
|
34
|
+
// Get chord notes considering the quality and the modifier
|
|
35
|
+
const chordNotes = getChordNotes(root, quality, modifier);
|
|
36
|
+
return chordNotes.every((note) => scaleNotes.includes(note));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Function to get the degrees of the scale
|
|
40
|
+
export function getScaleDegrees(armor: string, mode: string): string[] {
|
|
41
|
+
const scaleNotes = getScaleNotes(armor);
|
|
42
|
+
const modeIndex = modes.indexOf(mode);
|
|
43
|
+
if (modeIndex === -1) return [];
|
|
44
|
+
|
|
45
|
+
const rotatedScale = [...scaleNotes.slice(modeIndex), ...scaleNotes.slice(0, modeIndex)];
|
|
46
|
+
return rotatedScale.map((note, index) => {
|
|
47
|
+
const degree = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII'][index];
|
|
48
|
+
return `${degree} (${note})`;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Function to suggest valid chords based on armor and mode
|
|
53
|
+
export function suggestValidChords(armor: string, mode: string): string[] {
|
|
54
|
+
const scaleNotes = getScaleNotes(armor);
|
|
55
|
+
const degrees = getScaleDegrees(armor, mode);
|
|
56
|
+
// implement logic to suggest chords based on the scale notes and mode
|
|
57
|
+
return degrees;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Helper function to get chord notes based on root and quality
|
|
61
|
+
function getChordNotes(root: string, quality: string, modifier?: string): string[] {
|
|
62
|
+
const chromaticScale = ['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B'];
|
|
63
|
+
let rootIndex = chromaticScale.indexOf(root);
|
|
64
|
+
|
|
65
|
+
// Apply modifier to root if present
|
|
66
|
+
if (modifier) {
|
|
67
|
+
if (modifier === '♯') rootIndex = (rootIndex + 1) % 12;
|
|
68
|
+
if (modifier === '♭') rootIndex = (rootIndex - 1 + 12) % 12;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const modifiedRoot = chromaticScale[rootIndex];
|
|
72
|
+
|
|
73
|
+
let intervals: number[] = [0]; // Root note is always included
|
|
74
|
+
|
|
75
|
+
// Base triad
|
|
76
|
+
if (quality.includes('min')) {
|
|
77
|
+
intervals.push(3, 7); // Minor triad
|
|
78
|
+
} else if (quality.includes('aug')) {
|
|
79
|
+
intervals.push(4, 8); // Augmented triad
|
|
80
|
+
} else if (quality.includes('dim')) {
|
|
81
|
+
intervals.push(3, 6); // Diminished triad
|
|
82
|
+
} else {
|
|
83
|
+
intervals.push(4, 7); // Major triad (default)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Sevenths
|
|
87
|
+
if (quality.includes('7')) {
|
|
88
|
+
if (quality.includes('maj7')) {
|
|
89
|
+
intervals.push(11); // Major seventh
|
|
90
|
+
} else if (quality.includes('dim')) {
|
|
91
|
+
intervals.push(9); // Diminished seventh
|
|
92
|
+
} else {
|
|
93
|
+
intervals.push(10); // Minor seventh (dominant seventh)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Sixths
|
|
98
|
+
if (quality.includes('6')) {
|
|
99
|
+
intervals.push(9); // Major sixth
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Suspended chords
|
|
103
|
+
if (quality.includes('sus4')) {
|
|
104
|
+
intervals = intervals.filter((i) => i !== 4).concat(5);
|
|
105
|
+
} else if (quality.includes('sus2')) {
|
|
106
|
+
intervals = intervals.filter((i) => i !== 4).concat(2);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Add9, add11, add13
|
|
110
|
+
if (quality.includes('add9')) intervals.push(14);
|
|
111
|
+
if (quality.includes('add11')) intervals.push(17);
|
|
112
|
+
if (quality.includes('add13')) intervals.push(21);
|
|
113
|
+
|
|
114
|
+
// Remove duplicates and sort
|
|
115
|
+
intervals = [...new Set(intervals)].sort((a, b) => a - b);
|
|
116
|
+
|
|
117
|
+
return intervals.map((interval) => chromaticScale[(rootIndex + interval) % 12]);
|
|
118
|
+
}
|
package/src/lib/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// auto exports of entry components
|
|
2
2
|
export * from '$lib/types/types.js';
|
|
3
|
+
export * from '$lib/functions/rules.js';
|
|
3
4
|
export * from '$lib/functions/functions.svelte.js';
|
|
5
|
+
export * from '$lib/constants/constants.js';
|
|
4
6
|
export { default as ChordVisualization } from '$lib/components/ChordVisualization.svelte';
|
|
5
7
|
export { default as ChordTable } from '$lib/components/ChordTable.svelte';
|
|
6
8
|
export { default as CadencePanel } from '$lib/components/CadencePanel.svelte';
|
package/src/lib/types/types.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export type Chord = {
|
|
2
2
|
root: string;
|
|
3
3
|
quality: string;
|
|
4
|
-
mode?: string;
|
|
5
4
|
augDim?: string;
|
|
6
5
|
sus?: string;
|
|
7
6
|
sept?: string;
|
|
@@ -16,7 +15,14 @@ export type TimeSignature = {
|
|
|
16
15
|
|
|
17
16
|
export type ChordEntry = {
|
|
18
17
|
chord: Chord;
|
|
19
|
-
timeSignature?:
|
|
18
|
+
timeSignature?: { numerator: number; denominator: number };
|
|
19
|
+
armor: string;
|
|
20
|
+
mode?: string;
|
|
21
|
+
measureInfo: {
|
|
22
|
+
start: number;
|
|
23
|
+
end: number;
|
|
24
|
+
beatStart: number;
|
|
25
|
+
};
|
|
20
26
|
};
|
|
21
27
|
|
|
22
28
|
export type Cadence = {
|