Skip to content

Commit 71d5007

Browse files
authored
Refactor note handling with Hand and Finger abstractions (#15)
* Use Hand constant * Initial .editorconfig * Add fingers with mappings * Lint * Add FingeredNote abstraction * Add NotePosition abstraction * Refactor exercise retrieval to use NotePosition directly in practice sequence
1 parent fbe13ab commit 71d5007

15 files changed

+953
-856
lines changed

.editorconfig

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
insert_final_newline = true
7+
trim_trailing_whitespace = true
8+
9+
[*.gd]
10+
indent_style = tab
11+
indent_size = 4
12+
insert_trailing_comma = true

app/scripts/autoload/musical_constants.gd

-5
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,6 @@ const MIDI_TO_NOTE_PREFERRED = {
1919
const KEYS_PER_OCTAVE = 12
2020
const STARTING_MIDI_NOTE = 24 # Starting at C1
2121

22-
enum Hand {
23-
LEFT,
24-
RIGHT
25-
}
26-
2722
enum MusicKey {
2823
C,
2924
G,

app/scripts/constants/finger.gd

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const Hand = preload("res://scripts/constants/hand.gd").Hand
2+
3+
enum Finger {
4+
THUMB = 1,
5+
INDEX = 2,
6+
MIDDLE = 3,
7+
RING = 4,
8+
PINKY = 5
9+
}
10+
11+
var finger_names = {
12+
Finger.THUMB: "THUMB",
13+
Finger.INDEX: "INDEX",
14+
Finger.MIDDLE: "MIDDLE",
15+
Finger.RING: "RING",
16+
Finger.PINKY: "PINKY"
17+
}
18+
19+
func get_finger_number(finger: Finger) -> int:
20+
return int(finger)
21+
22+
func get_finger_name(finger: Finger) -> String:
23+
return finger_names[finger]

app/scripts/constants/hand.gd

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
enum Hand {
2+
RIGHT_HAND,
3+
LEFT_HAND,
4+
}

app/scripts/exercise_manager.gd

+43-36
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
extends Control
22

3+
const Hand = preload("res://scripts/constants/hand.gd").Hand
4+
const FingeredNote = preload("res://scripts/models/fingered_note.gd")
5+
const NotePosition = preload("res://scripts/models/note_position.gd")
6+
37
# Signals
48
signal clear_highlighted_keys
59
signal exercise_sequence_created(sequence: PracticeSequence)
610
signal sequence_updated(current_position: int)
711
signal sequence_completed
812
signal note_validated(success: bool)
9-
signal highlight_note_by_name(note_name: String, hand: MusicalConstants.Hand)
13+
signal highlight_note_by_name(note_name: String, hand: Hand)
1014
signal unhighlight_note_by_name(note_name: String)
1115
signal clear_finger_indicators
12-
signal add_finger_indicator(note: PianoNote, is_current: bool)
16+
signal add_finger_indicator(note: FingeredNote, is_current: bool)
1317

1418
# Node references
1519
@onready var hand_dropdown = $HBoxContainer/HandDropdown
@@ -63,29 +67,38 @@ func _on_hand_selected(index):
6367
_update_exercise()
6468

6569
func _update_exercise():
66-
print("Updating exercise")
6770
emit_signal("clear_highlighted_keys")
6871

69-
var exercise_type = exercise_type_dropdown.get_item_text(exercise_type_dropdown.selected)
72+
var exercise_type_str = exercise_type_dropdown.get_item_text(exercise_type_dropdown.selected)
73+
var exercise_type: PracticeSequence.ExerciseType
74+
75+
if exercise_type_str == "Scales":
76+
exercise_type = PracticeSequence.ExerciseType.SCALES
77+
elif exercise_type_str == "Chords":
78+
exercise_type = PracticeSequence.ExerciseType.CHORDS
79+
elif exercise_type_str == "Arpeggios":
80+
exercise_type = PracticeSequence.ExerciseType.ARPEGGIOS
81+
else:
82+
exercise_type = PracticeSequence.ExerciseType.SCALES
83+
7084
var music_key_str = music_key_dropdown.get_item_text(music_key_dropdown.selected)
7185
var music_key = null
7286
for key in MusicalConstants.MusicKey.values():
7387
if MusicalConstants.MUSIC_KEY_STRINGS[key] == music_key_str:
7488
music_key = key
7589
break
7690

77-
var hand = MusicalConstants.Hand.RIGHT if hand_dropdown.get_item_text(hand_dropdown.selected).begins_with("Right") else MusicalConstants.Hand.LEFT
78-
var hand_name = "right_hand" if hand == MusicalConstants.Hand.RIGHT else "left_hand"
91+
var hand = Hand.RIGHT_HAND if hand_dropdown.get_item_text(hand_dropdown.selected).begins_with("Right") else Hand.LEFT_HAND
7992

8093
var exercises = {
81-
"Scales": "create_scale",
82-
"Chords": "create_chord_inversions",
83-
"Arpeggios": "create_arpeggios"
94+
PracticeSequence.ExerciseType.SCALES: "create_scale",
95+
PracticeSequence.ExerciseType.CHORDS: "create_chord_inversions",
96+
PracticeSequence.ExerciseType.ARPEGGIOS: "create_arpeggios"
8497
}
8598

8699
if exercises.has(exercise_type):
87100
var exercise_method = exercises[exercise_type]
88-
var exercise_sequence = self.call(exercise_method, music_key, hand_name, hand)
101+
var exercise_sequence = self.call(exercise_method, music_key, hand)
89102
set_sequence(exercise_sequence)
90103

91104
# Set a new sequence and reset state
@@ -106,7 +119,7 @@ func validate_input(midi_notes: Array[int]) -> bool:
106119
if not current_sequence or current_position >= current_sequence.sequence.size():
107120
return false
108121

109-
var current_notes = current_sequence.sequence[current_position]
122+
var current_notes = current_sequence.sequence[current_position].notes
110123
# First validate that all played notes are part of the expected chord
111124
for midi_note in midi_notes:
112125
var note_valid = false
@@ -146,7 +159,7 @@ func advance_sequence():
146159

147160
if current_position < current_sequence.sequence.size() - 1:
148161
current_position += 1
149-
var current_notes = current_sequence.sequence[current_position]
162+
var current_notes = current_sequence.sequence[current_position].notes
150163
var note_names = []
151164
for note in current_notes:
152165
note_names.append(note.pitch)
@@ -166,21 +179,21 @@ func update_display():
166179
emit_signal("clear_finger_indicators")
167180

168181
if current_position < current_sequence.sequence.size():
169-
var current_notes = current_sequence.sequence[current_position]
182+
var current_notes = current_sequence.sequence[current_position].notes
170183
for note in current_notes:
171184
emit_signal("add_finger_indicator", note, true)
172185

173186
# Highlight current chord notes
174187
func highlight_current_note():
175188
if current_position < current_sequence.sequence.size():
176-
var current_notes = current_sequence.sequence[current_position]
189+
var current_notes = current_sequence.sequence[current_position].notes
177190
for note in current_notes:
178191
emit_signal("highlight_note_by_name", note.pitch, note.hand)
179192

180193
# Remove highlighting from current chord notes
181194
func unhighlight_current_note():
182195
if current_position < current_sequence.sequence.size():
183-
var current_notes = current_sequence.sequence[current_position]
196+
var current_notes = current_sequence.sequence[current_position].notes
184197
for note in current_notes:
185198
emit_signal("unhighlight_note_by_name", note.pitch)
186199

@@ -199,38 +212,32 @@ func midi_to_note_name(midi_note: int) -> String:
199212
return MusicalConstants.MIDI_TO_NOTE_PREFERRED[note_index] + str(octave)
200213

201214
# Exercise creation methods
202-
func create_scale(music_key: MusicalConstants.MusicKey, hand_name: String, hand: MusicalConstants.Hand) -> PracticeSequence:
215+
func create_scale(music_key: MusicalConstants.MusicKey, hand: Hand) -> PracticeSequence:
203216
var practice_sequence = PracticeSequence.new()
204-
practice_sequence.exercise_type = "scale"
217+
practice_sequence.exercise_type = PracticeSequence.ExerciseType.SCALES
205218

206-
var scale_notes = scales.get_exercise(music_key, hand_name)
207-
for note_data in scale_notes:
208-
var note = PianoNote.new(note_data[0], hand, note_data[1])
209-
practice_sequence.add_position([note])
219+
var exercise = scales.get_exercise(music_key, hand)
220+
for position in exercise:
221+
practice_sequence.add_position(position)
210222

211223
return practice_sequence
212224

213-
func create_chord_inversions(music_key: MusicalConstants.MusicKey, hand_name: String, hand: MusicalConstants.Hand) -> PracticeSequence:
225+
func create_chord_inversions(music_key: MusicalConstants.MusicKey, hand: Hand) -> PracticeSequence:
214226
var practice_sequence = PracticeSequence.new()
215-
practice_sequence.exercise_type = "chord_inversions"
227+
practice_sequence.exercise_type = PracticeSequence.ExerciseType.CHORDS
216228

217-
var chord_notes = chords.get_exercise(music_key, hand_name)
218-
for chord in chord_notes:
219-
var chord_position: Array[PianoNote] = []
220-
for note_data in chord:
221-
var note = PianoNote.new(note_data[0], hand, note_data[1])
222-
chord_position.append(note)
223-
practice_sequence.add_position(chord_position)
229+
var exercise = chords.get_exercise(music_key, hand)
230+
for position in exercise:
231+
practice_sequence.add_position(position)
224232

225233
return practice_sequence
226234

227-
func create_arpeggios(music_key: MusicalConstants.MusicKey, hand_name: String, hand: MusicalConstants.Hand) -> PracticeSequence:
235+
func create_arpeggios(music_key: MusicalConstants.MusicKey, hand: Hand) -> PracticeSequence:
228236
var practice_sequence = PracticeSequence.new()
229-
practice_sequence.exercise_type = "arpeggio"
237+
practice_sequence.exercise_type = PracticeSequence.ExerciseType.ARPEGGIOS
230238

231-
var arpeggio_notes = arpeggios.get_exercise(music_key, hand_name)
232-
for note_data in arpeggio_notes:
233-
var note = PianoNote.new(note_data[0], hand, note_data[1])
234-
practice_sequence.add_position([note])
239+
var exercise = arpeggios.get_exercise(music_key, hand)
240+
for position in exercise:
241+
practice_sequence.add_position(position)
235242

236243
return practice_sequence

0 commit comments

Comments
 (0)