Skip to content

Commit 6e138d9

Browse files
authored
Enhance exercise display with hand-based coloring refactor music keys using constants (#11)
* Add hand selector * Initial draft LH exercises * Dynamically load LH exercises * Refactor exercise loading and mapping to use preload and structured exercise data * Add color constants and refactor hand handling in exercise manager * Refactor hand handling to use enum for left and right hands across exercise management and display * Add MusicKey enum and refactor exercise management to use structured music keys
1 parent 2cd167d commit 6e138d9

File tree

12 files changed

+741
-76
lines changed

12 files changed

+741
-76
lines changed

app/project.godot

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ config/icon="res://icon.svg"
1717
[autoload]
1818

1919
MusicalConstants="*res://scripts/autoload/musical_constants.gd"
20+
Colors="*res://scripts/autoload/colors.gd"
2021

2122
[display]
2223

app/scenes/piano.tscn

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ script = ExtResource("6_7h8j9")
5252
layout_mode = 0
5353
anchor_right = 1.0
5454

55+
[node name="HandDropdown" type="OptionButton" parent="ExerciseManager/HBoxContainer"]
56+
layout_mode = 2
57+
theme_override_font_sizes/font_size = 20
58+
5559
[node name="ExerciseTypeDropdown" type="OptionButton" parent="ExerciseManager/HBoxContainer"]
5660
layout_mode = 2
5761
theme_override_font_sizes/font_size = 20

app/scripts/autoload/colors.gd

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
extends Node
2+
3+
const LEFT_HAND_COLOR = Color(0.0, 0.5, 1.0, 1.0) # Blue
4+
const RIGHT_HAND_COLOR = Color(1.0, 0.8, 0.0, 1.0) # Yellow
5+
const BACKGROUND_COLOR = Color(0.1, 0.1, 0.1, 1.0)

app/scripts/autoload/musical_constants.gd

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,38 @@ const MIDI_TO_NOTE_PREFERRED = {
1818

1919
const KEYS_PER_OCTAVE = 12
2020
const STARTING_MIDI_NOTE = 24 # Starting at C1
21+
22+
enum Hand {
23+
LEFT,
24+
RIGHT
25+
}
26+
27+
enum MusicKey {
28+
C,
29+
G,
30+
D,
31+
A,
32+
E,
33+
B,
34+
F_SHARP,
35+
C_SHARP,
36+
F,
37+
B_FLAT,
38+
E_FLAT,
39+
A_FLAT
40+
}
41+
42+
const MUSIC_KEY_STRINGS = {
43+
MusicKey.C: "C",
44+
MusicKey.G: "G",
45+
MusicKey.D: "D",
46+
MusicKey.A: "A",
47+
MusicKey.E: "E",
48+
MusicKey.B: "B",
49+
MusicKey.F_SHARP: "F#",
50+
MusicKey.C_SHARP: "C#",
51+
MusicKey.F: "F",
52+
MusicKey.B_FLAT: "Bb",
53+
MusicKey.E_FLAT: "Eb",
54+
MusicKey.A_FLAT: "Ab"
55+
}

app/scripts/exercise_manager.gd

Lines changed: 37 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,57 +4,30 @@ extends Control
44
signal clear_highlighted_keys
55

66
# Node references
7+
@onready var hand_dropdown = $HBoxContainer/HandDropdown
78
@onready var exercise_type_dropdown = $HBoxContainer/ExerciseTypeDropdown
89
@onready var music_key_dropdown = $HBoxContainer/KeyDropdown
910
@onready var sequence_manager = get_parent().get_node("SequenceManager")
1011

1112
# Load exercises
12-
@onready var scales = load("res://scripts/exercises/scales_major.gd").new()
13-
@onready var chords = load("res://scripts/exercises/chords_major.gd").new()
14-
@onready var arpeggios = load("res://scripts/exercises/arpeggios_major.gd").new()
15-
16-
# Named entity for exercise map
17-
class ExerciseMap:
18-
var scales: Array
19-
var chords: Array
20-
var arpeggios: Array
21-
22-
func _init(_scales: Array, _chords: Array, _arpeggios: Array):
23-
scales = _scales
24-
chords = _chords
25-
arpeggios = _arpeggios
26-
27-
# Helper method to create exercise maps
28-
func create_exercise_map(key: String) -> ExerciseMap:
29-
return ExerciseMap.new(
30-
scales.get(key.to_snake_case() + "_major_rh_1oct"),
31-
chords.get(key.to_snake_case() + "_major_rh_inversions"),
32-
arpeggios.get(key.to_snake_case() + "_major_rh_arpeggios")
33-
)
34-
35-
# Key to exercise mapping
36-
@onready var key_to_exercises = {
37-
"C": create_exercise_map("c"),
38-
"G": create_exercise_map("g"),
39-
"D": create_exercise_map("d"),
40-
"A": create_exercise_map("a"),
41-
"E": create_exercise_map("e"),
42-
"B": create_exercise_map("b"),
43-
"F#": create_exercise_map("f_sharp"),
44-
"C#": create_exercise_map("c_sharp"),
45-
"F": create_exercise_map("f"),
46-
"Bb": create_exercise_map("b_flat"),
47-
"Eb": create_exercise_map("e_flat"),
48-
"Ab": create_exercise_map("a_flat")
49-
}
13+
@onready var scales = preload("res://scripts/exercises/scales_major.gd").new()
14+
@onready var chords = preload("res://scripts/exercises/chords_major.gd").new()
15+
@onready var arpeggios = preload("res://scripts/exercises/arpeggios_major.gd").new()
5016

5117
func _ready():
18+
hand_dropdown.connect("item_selected", _on_hand_selected)
5219
exercise_type_dropdown.connect("item_selected", _on_exercise_type_selected)
5320
music_key_dropdown.connect("item_selected", _on_key_selected)
5421
_initialize_dropdowns()
5522
call_deferred("_update_exercise")
5623

5724
func _initialize_dropdowns():
25+
# Initialize hand selection
26+
hand_dropdown.clear()
27+
hand_dropdown.add_item("Right Hand")
28+
hand_dropdown.add_item("Left Hand")
29+
30+
# Initialize exercise types
5831
exercise_type_dropdown.clear()
5932
exercise_type_dropdown.add_item("Scales")
6033
exercise_type_dropdown.add_item("Chords")
@@ -64,9 +37,8 @@ func _initialize_dropdowns():
6437

6538
func _update_key_dropdown():
6639
music_key_dropdown.clear()
67-
var music_keys = ["C", "G", "D", "A", "E", "B", "F#", "C#", "F", "Bb", "Eb", "Ab"]
68-
for music_key in music_keys:
69-
music_key_dropdown.add_item(music_key)
40+
for key in MusicalConstants.MusicKey.values():
41+
music_key_dropdown.add_item(MusicalConstants.MUSIC_KEY_STRINGS[key])
7042

7143
func _on_exercise_type_selected(index):
7244
_update_key_dropdown()
@@ -75,12 +47,24 @@ func _on_exercise_type_selected(index):
7547
func _on_key_selected(index):
7648
_update_exercise()
7749

50+
func _on_hand_selected(index):
51+
_update_exercise()
52+
7853
func _update_exercise():
7954
emit_signal("clear_highlighted_keys")
8055

8156
var exercise_type = exercise_type_dropdown.get_item_text(exercise_type_dropdown.selected)
82-
var music_key = music_key_dropdown.get_item_text(music_key_dropdown.selected)
83-
print(exercise_type, music_key)
57+
var music_key_str = music_key_dropdown.get_item_text(music_key_dropdown.selected)
58+
var music_key = null
59+
for key in MusicalConstants.MusicKey.values():
60+
if MusicalConstants.MUSIC_KEY_STRINGS[key] == music_key_str:
61+
music_key = key
62+
break
63+
64+
var hand = MusicalConstants.Hand.RIGHT if hand_dropdown.get_item_text(hand_dropdown.selected).begins_with("Right") else MusicalConstants.Hand.LEFT
65+
var hand_name = "right_hand" if hand == MusicalConstants.Hand.RIGHT else "left_hand"
66+
67+
print(exercise_type, music_key_str, hand_name)
8468
var exercises = {
8569
"Scales": "create_scale",
8670
"Chords": "create_chord_inversions",
@@ -89,45 +73,42 @@ func _update_exercise():
8973

9074
if exercises.has(exercise_type):
9175
var exercise_method = exercises[exercise_type]
92-
var exercise_sequence = self.call(exercise_method, music_key)
76+
var exercise_sequence = self.call(exercise_method, music_key, hand_name, hand)
9377
sequence_manager.set_sequence(exercise_sequence)
9478

9579
# Exercise creation methods
96-
func create_scale(music_key: String) -> PracticeSequence:
80+
func create_scale(music_key: MusicalConstants.MusicKey, hand_name: String, hand: MusicalConstants.Hand) -> PracticeSequence:
9781
var practice_sequence = PracticeSequence.new()
9882
practice_sequence.exercise_type = "scale"
99-
100-
var scale_notes = key_to_exercises[music_key].scales
10183

84+
var scale_notes = scales.get_exercise(music_key, hand_name)
10285
for note_data in scale_notes:
103-
var note = PianoNote.new(note_data[0], "R", note_data[1])
86+
var note = PianoNote.new(note_data[0], hand, note_data[1])
10487
practice_sequence.add_position([note])
10588

10689
return practice_sequence
10790

108-
func create_chord_inversions(music_key: String) -> PracticeSequence:
91+
func create_chord_inversions(music_key: MusicalConstants.MusicKey, hand_name: String, hand: MusicalConstants.Hand) -> PracticeSequence:
10992
var practice_sequence = PracticeSequence.new()
11093
practice_sequence.exercise_type = "chord_inversions"
11194

112-
var chord_notes = key_to_exercises[music_key].chords
113-
95+
var chord_notes = chords.get_exercise(music_key, hand_name)
11496
for chord in chord_notes:
11597
var chord_position: Array[PianoNote] = []
11698
for note_data in chord:
117-
var note = PianoNote.new(note_data[0], "R", note_data[1])
99+
var note = PianoNote.new(note_data[0], hand, note_data[1])
118100
chord_position.append(note)
119101
practice_sequence.add_position(chord_position)
120102

121103
return practice_sequence
122104

123-
func create_arpeggios(music_key: String) -> PracticeSequence:
105+
func create_arpeggios(music_key: MusicalConstants.MusicKey, hand_name: String, hand: MusicalConstants.Hand) -> PracticeSequence:
124106
var practice_sequence = PracticeSequence.new()
125107
practice_sequence.exercise_type = "arpeggio"
126108

127-
var arpeggio_notes = key_to_exercises[music_key].arpeggios
128-
109+
var arpeggio_notes = arpeggios.get_exercise(music_key, hand_name)
129110
for note_data in arpeggio_notes:
130-
var note = PianoNote.new(note_data[0], "R", note_data[1])
111+
var note = PianoNote.new(note_data[0], hand, note_data[1])
131112
practice_sequence.add_position([note])
132113

133114
return practice_sequence

app/scripts/exercises/arpeggios_major.gd

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
class_name ArpeggioExercises extends Resource
12
# Right hand ascending arpeggios with correct fingerings for all major triads
23

34
var c_major_rh_arpeggios = [
@@ -82,4 +83,147 @@ var a_flat_major_rh_arpeggios = [
8283
["C5", 2], # Index
8384
["Eb5", 3], # Middle
8485
["Ab5", 5] # Pinky
85-
]
86+
]
87+
88+
# Left hand ascending arpeggios with correct fingerings for all major triads
89+
90+
var c_major_lh_arpeggios = [
91+
["C3", 5], # Pinky
92+
["E3", 3], # Middle
93+
["G3", 2], # Index
94+
["C4", 1] # Thumb
95+
]
96+
97+
var g_major_lh_arpeggios = [
98+
["G3", 5], # Pinky
99+
["B3", 3], # Middle
100+
["D4", 2], # Index
101+
["G4", 1] # Thumb
102+
]
103+
104+
var d_major_lh_arpeggios = [
105+
["D3", 5], # Pinky
106+
["F#3", 3], # Middle
107+
["A3", 2], # Index
108+
["D4", 1] # Thumb
109+
]
110+
111+
var a_major_lh_arpeggios = [
112+
["A3", 5], # Pinky
113+
["C#4", 3], # Middle
114+
["E4", 2], # Index
115+
["A4", 1] # Thumb
116+
]
117+
118+
var e_major_lh_arpeggios = [
119+
["E3", 5], # Pinky
120+
["G#3", 3], # Middle
121+
["B3", 2], # Index
122+
["E4", 1] # Thumb
123+
]
124+
125+
var b_major_lh_arpeggios = [
126+
["B2", 5], # Pinky
127+
["D#3", 3], # Middle
128+
["F#3", 2], # Index
129+
["B3", 1] # Thumb
130+
]
131+
132+
var f_sharp_major_lh_arpeggios = [
133+
["F#3", 5], # Pinky
134+
["A#3", 3], # Middle
135+
["C#4", 2], # Index
136+
["F#4", 1] # Thumb
137+
]
138+
139+
var c_sharp_major_lh_arpeggios = [
140+
["C#3", 5], # Pinky
141+
["E#3", 3], # Middle
142+
["G#3", 2], # Index
143+
["C#4", 1] # Thumb
144+
]
145+
146+
var f_major_lh_arpeggios = [
147+
["F3", 5], # Pinky
148+
["A3", 3], # Middle
149+
["C4", 2], # Index
150+
["F4", 1] # Thumb
151+
]
152+
153+
var b_flat_major_lh_arpeggios = [
154+
["Bb2", 5], # Pinky
155+
["D3", 3], # Middle
156+
["F3", 2], # Index
157+
["Bb3", 1] # Thumb
158+
]
159+
160+
var e_flat_major_lh_arpeggios = [
161+
["Eb3", 5], # Pinky
162+
["G3", 3], # Middle
163+
["Bb3", 2], # Index
164+
["Eb4", 1] # Thumb
165+
]
166+
167+
var a_flat_major_lh_arpeggios = [
168+
["Ab3", 5], # Pinky
169+
["C4", 3], # Middle
170+
["Eb4", 2], # Index
171+
["Ab4", 1] # Thumb
172+
]
173+
174+
var exercises = {
175+
MusicalConstants.MusicKey.C: {
176+
"right_hand": c_major_rh_arpeggios,
177+
"left_hand": c_major_lh_arpeggios
178+
},
179+
MusicalConstants.MusicKey.G: {
180+
"right_hand": g_major_rh_arpeggios,
181+
"left_hand": g_major_lh_arpeggios
182+
},
183+
MusicalConstants.MusicKey.D: {
184+
"right_hand": d_major_rh_arpeggios,
185+
"left_hand": d_major_lh_arpeggios
186+
},
187+
MusicalConstants.MusicKey.A: {
188+
"right_hand": a_major_rh_arpeggios,
189+
"left_hand": a_major_lh_arpeggios
190+
},
191+
MusicalConstants.MusicKey.E: {
192+
"right_hand": e_major_rh_arpeggios,
193+
"left_hand": e_major_lh_arpeggios
194+
},
195+
MusicalConstants.MusicKey.B: {
196+
"right_hand": b_major_rh_arpeggios,
197+
"left_hand": b_major_lh_arpeggios
198+
},
199+
MusicalConstants.MusicKey.F_SHARP: {
200+
"right_hand": f_sharp_major_rh_arpeggios,
201+
"left_hand": f_sharp_major_lh_arpeggios
202+
},
203+
MusicalConstants.MusicKey.C_SHARP: {
204+
"right_hand": c_sharp_major_rh_arpeggios,
205+
"left_hand": c_sharp_major_lh_arpeggios
206+
},
207+
MusicalConstants.MusicKey.F: {
208+
"right_hand": f_major_rh_arpeggios,
209+
"left_hand": f_major_lh_arpeggios
210+
},
211+
MusicalConstants.MusicKey.B_FLAT: {
212+
"right_hand": b_flat_major_rh_arpeggios,
213+
"left_hand": b_flat_major_lh_arpeggios
214+
},
215+
MusicalConstants.MusicKey.E_FLAT: {
216+
"right_hand": e_flat_major_rh_arpeggios,
217+
"left_hand": e_flat_major_lh_arpeggios
218+
},
219+
MusicalConstants.MusicKey.A_FLAT: {
220+
"right_hand": a_flat_major_rh_arpeggios,
221+
"left_hand": a_flat_major_lh_arpeggios
222+
}
223+
}
224+
225+
func get_exercise(key: MusicalConstants.MusicKey, hand: String) -> Array:
226+
return exercises[key][hand]
227+
228+
func has_exercise(key: MusicalConstants.MusicKey, hand: String) -> bool:
229+
return exercises.has(key) and exercises[key].has(hand)

0 commit comments

Comments
 (0)