"C#": 1, "Db": 1, "D": 2, "D#": 3, "Eb": 3, "E": 4, "Fb": 4, "E#": 5, "F": 5, "F#": 6, "Gb": 6, "G": 7, "G#": 8, "Ab": 8, "A": 9, "A#": 10, "Bb": 10, "B": 11, "Cb": 11, "B#": 0 ] let semitoneToNote: [Int: (sharp: String, flat: String)] = [ 0: ("C", "C"), 1: ("C#", "Db"), 2: ("D", "D"), 3: ("D#", "Eb"), 4: ("E", "Fb"), 5: ("F", "F"), 6: ("F#", "Gb"), 7: ("G", "G"), 8: ("G#", "Ab"), 9: ("A", "A"), 10: ("A#", "Bb"), 11: ("B", "Cb") ] func chordNotes(chord: String) -> [String] { let root = String(chord.prefix(while: { ["C", "D", "E", "F", "G", "A", "B"].contains($0) || $0 == "#" || $0 == "b" })) let suffix = String(chord.dropFirst(root.count)) guard let rootSemitone = noteToSemitone[root] else { return [] } var intervals = switch suffix { case "m" : [0, 3, 7] case "7" : [0, 4, 7, 10] case "m7" : [0, 3, 7, 10] case "M7" : [0, 4, 7, 11] case "add9" : [0, 4, 7, 14] case "madd9": [0, 3, 7, 14] case "dim" : [0, 3, 6] case "aug" : [0, 4, 8] case "m7b5" : [0, 3, 6, 10] default : [0, 4, 7] } let notes = intervals.map { let semitone = ($0 + rootSemitone) % 12 return root.contains("#") ? semitoneToNote[semitone]!.sharp : semitoneToNote[semitone]!.flat } return notes }