Skip to content

Commit 7b7f649

Browse files
committed
Implement flat chords
1 parent e368888 commit 7b7f649

File tree

4 files changed

+41
-12
lines changed

4 files changed

+41
-12
lines changed

melodica_notes/chords.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from melodica_notes.scales import NOTES, scale
1+
from melodica_notes.scales import FLAT_NOTES, SHARP_NOTES, scale
22

33

44
def minor(tonic_note: str) -> tuple[list[str], list[str]]:
@@ -51,9 +51,19 @@ def semitone(tonic_note: str, *, interval: int) -> str:
5151
>>> semitone('A', interval=-1)
5252
'G#'
5353
"""
54-
position = NOTES.index(tonic_note.upper()) + interval
54+
if len(tonic_note) == 1:
55+
tonic_note = tonic_note.upper()
56+
else:
57+
tonic_note = tonic_note[0].upper() + tonic_note[1].lower()
58+
59+
try:
60+
position = SHARP_NOTES.index(tonic_note) + interval
61+
notes = SHARP_NOTES
62+
except ValueError:
63+
position = FLAT_NOTES.index(tonic_note) + interval
64+
notes = FLAT_NOTES
5565

56-
return NOTES[position % 12]
66+
return notes[position % 12]
5767

5868

5969
def triad(tonic_note: str, scale_type: str) -> list[str]:

melodica_notes/scales.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
NOTES = "C C# D D# E F F# G G# A A# B".split()
1+
NOTES = "C C# Db D D# Eb E F F# Gb G G# Ab A A# Bb B".split()
2+
SHARP_NOTES = "C C# D D# E F F# G G# A A# B".split()
3+
FLAT_NOTES = "C Db D Eb E F Gb G Ab A Bb B".split()
24
SCALES = {"major": (0, 2, 4, 5, 7, 9, 11), "minor": (0, 2, 3, 5, 7, 8, 10)}
35

46

@@ -25,10 +27,16 @@ def scale(tonic_note: str, scale_type: str) -> dict[str, list[str]]:
2527
{'notes': ['A', 'B', 'C', 'D', 'E', 'F', 'G'], 'degrees': ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII']}
2628
"""
2729
tonic_note = tonic_note.upper()
30+
notes = SHARP_NOTES
31+
32+
if len(tonic_note) > 1:
33+
tonic_note = tonic_note[0] + tonic_note[1].lower()
34+
if tonic_note[1] == "b":
35+
notes = FLAT_NOTES
2836

2937
try:
3038
intervals = SCALES[scale_type]
31-
tonic_position = NOTES.index(tonic_note)
39+
tonic_position = notes.index(tonic_note)
3240
except ValueError:
3341
raise ValueError(
3442
f"This musical note does not exist. Please use one of these: {NOTES}")
@@ -42,6 +50,6 @@ def scale(tonic_note: str, scale_type: str) -> dict[str, list[str]]:
4250

4351
for interval in intervals:
4452
note = (tonic_position + interval) % 12
45-
temp.append(NOTES[note])
53+
temp.append(notes[note])
4654

4755
return {"notes": temp, "degrees": ["I", "II", "III", "IV", "V", "VI", "VII"]}

tests/test_chord.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,18 @@
1111
("Cdim", ["C", "D#", "F#"]),
1212
("C+", ["C", "E", "G#"]),
1313
("Cm+", ["C", "D#", "G#"]),
14+
1415
("F#", ["F#", "A#", "C#"]),
16+
("F#m", ["F#", "A", "C#"]),
17+
("F#dim", ["F#", "A", "C"]),
18+
("F#+", ["F#", "A#", "D"]),
19+
("F#m+", ["F#", "A", "D"]),
20+
21+
("Gb", ["Gb", "Bb", "Db"]),
22+
("Gbm", ["Gb", "A", "Db"]),
23+
("Gbdim", ["Gb", "A", "C"]),
24+
("Gb+", ["Gb", "Bb", "D"]),
25+
("Gbm+", ["Gb", "A", "D"]),
1526
]
1627
)
1728
def test_chords_return_corresponding_notes(tonic_note, expected):

tests/test_scale.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,28 +42,28 @@ def test_raises_error_for_invalid_scale_type():
4242
('C', 'major', ['C', 'D', 'E', 'F', 'G', 'A', 'B']),
4343
('C#', 'major', ['C#', 'D#', 'F', 'F#', 'G#', 'A#', 'C']),
4444
('D', 'major', ['D', 'E', 'F#', 'G', 'A', 'B', 'C#']),
45-
('D#', 'major', ['D#', 'F', 'G', 'G#', 'A#', 'C', 'D']),
45+
('Eb', 'major', ['Eb', 'F', 'G', 'Ab', 'Bb', 'C', 'D']),
4646
('E', 'major', ['E', 'F#', 'G#', 'A', 'B', 'C#', 'D#']),
4747
('F', 'major', ['F', 'G', 'A', 'A#', 'C', 'D', 'E']),
4848
('F#', 'major', ['F#', 'G#', 'A#', 'B', 'C#', 'D#', 'F']),
4949
('G', 'major', ['G', 'A', 'B', 'C', 'D', 'E', 'F#']),
50-
('G#', 'major', ['G#', 'A#', 'C', 'C#', 'D#', 'F', 'G']),
50+
('Ab', 'major', ['Ab', 'Bb', 'C', 'Db', 'Eb', 'F', 'G']),
5151
('A', 'major', ['A', 'B', 'C#', 'D', 'E', 'F#', 'G#']),
52-
('A#', 'major', ['A#', 'C', 'D', 'D#', 'F', 'G', 'A']),
52+
('Bb', 'major', ['Bb', 'C', 'D', 'Eb', 'F', 'G', 'A']),
5353
('B', 'major', ['B', 'C#', 'D#', 'E', 'F#', 'G#', 'A#']),
5454
5555
# Minor Scales
5656
('C', 'minor', ['C', 'D', 'D#', 'F', 'G', 'G#', 'A#']),
5757
('C#', 'minor', ['C#', 'D#', 'E', 'F#', 'G#', 'A', 'B']),
5858
('D', 'minor', ['D', 'E', 'F', 'G', 'A', 'A#', 'C']),
59-
('D#', 'minor', ['D#', 'F', 'F#', 'G#', 'A#', 'B', 'C#']),
59+
('Eb', 'minor', ['Eb', 'F', 'Gb', 'Ab', 'Bb', 'B', 'Db']),
6060
('E', 'minor', ['E', 'F#', 'G', 'A', 'B', 'C', 'D']),
6161
('F', 'minor', ['F', 'G', 'G#', 'A#', 'C', 'C#', 'D#']),
6262
('F#', 'minor', ['F#', 'G#', 'A', 'B', 'C#', 'D', 'E']),
6363
('G', 'minor', ['G', 'A', 'A#', 'C', 'D', 'D#', 'F']),
64-
('G#', 'minor', ['G#', 'A#', 'B', 'C#', 'D#', 'E', 'F#']),
64+
('Ab', 'minor', ['Ab', 'Bb', 'B', 'Db', 'Eb', 'E', 'Gb']),
6565
('A', 'minor', ['A', 'B', 'C', 'D', 'E', 'F', 'G']),
66-
('A#', 'minor', ['A#', 'C', 'C#', 'D#', 'F', 'F#', 'G#']),
66+
('Bb', 'minor', ['Bb', 'C', 'Db', 'Eb', 'F', 'Gb', 'Ab']),
6767
('B', 'minor', ['B', 'C#', 'D', 'E', 'F#', 'G', 'A']),
6868
],
6969
)

0 commit comments

Comments
 (0)