-
-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes for slash notation and getRankedChords using circle of 5ths and 4ths #34
Changes from 3 commits
d8ba95f
f9a7f18
2d66418
2a24c31
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -125,6 +125,29 @@ extension Chord: CustomStringConvertible { | |
public var description: String { | ||
return "\(root)\(type)" | ||
} | ||
|
||
/// Name of chord using slash chords | ||
public var slashDescription: String { | ||
if inversion > 0 { | ||
return "\(root)\(type)/\(bassNote)" | ||
} else { | ||
return description | ||
} | ||
} | ||
|
||
/// Bass Note computed from inversion and root note | ||
/// Useful for custom rendering of slash notation | ||
public var bassNote: NoteClass { | ||
switch inversion { | ||
case 1...4: | ||
if let bass = root.canonicalNote.shiftUp(type.intervals[inversion - 1]) { | ||
return bass.noteClass | ||
} | ||
default: | ||
break | ||
} | ||
return root.canonicalNote.noteClass | ||
} | ||
} | ||
|
||
extension Chord { | ||
|
@@ -146,31 +169,24 @@ extension Chord { | |
|
||
/// Get chords that match a set of pitches, ranking by least number of accidentals | ||
public static func getRankedChords(from pitchSet: PitchSet) -> [Chord] { | ||
var cNotes: [Note] = [] | ||
var bNotes: [Note] = [] | ||
var sNotes: [Note] = [] | ||
var noteArrays: Set<[Note]> = [] | ||
var returnArray: [Chord] = [] | ||
|
||
for pitch in pitchSet.array { | ||
cNotes.append(Note(pitch: pitch, key: .C)) | ||
bNotes.append(Note(pitch: pitch, key: .Cb)) | ||
sNotes.append(Note(pitch: pitch, key: .Cs)) | ||
} | ||
returnArray.append(contentsOf: Chord.getRankedChords(from: cNotes)) | ||
|
||
for chord in Chord.getRankedChords(from: sNotes) { | ||
if !returnArray.contains(chord) { | ||
returnArray.append(chord) | ||
} | ||
for key in Key.circleOfFifths { | ||
noteArrays.insert(pitchSet.array.map { Note(pitch: $0, key: key) }) | ||
} | ||
for chord in Chord.getRankedChords(from: bNotes) { | ||
if !returnArray.contains(chord) { | ||
returnArray.append(chord) | ||
} | ||
|
||
for key in Key.circleOfFourths { | ||
noteArrays.insert(pitchSet.array.map { Note(pitch: $0, key: key) }) | ||
} | ||
for chord in returnArray { | ||
print(chord, chord.accidentalCount) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trailing Whitespace Violation: Lines should not have trailing whitespace. (trailing_whitespace) |
||
for noteArray in noteArrays { | ||
returnArray.append(contentsOf: Chord.getRankedChords(from: noteArray)) | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trailing Whitespace Violation: Lines should not have trailing whitespace. (trailing_whitespace) |
||
// sort by alphabetical to prevent nondeterminstic sorting from same number of accdientals | ||
returnArray.sort { $0.root.accidental < $1.root.accidental } | ||
|
||
// order the array by least number of accidentals | ||
returnArray.sort { $0.accidentalCount < $1.accidentalCount } | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,7 +36,7 @@ public class ChordTable { | |
for chordType in ChordType.allCases { | ||
ChordTable.generateChords(type: chordType, &r) | ||
} | ||
print("generated \(r.count) chords") | ||
//print("generated \(r.count) chords") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment Spacing Violation: Prefer at least one space after slashes for comments. (comment_spacing) |
||
return r | ||
} | ||
|
||
|
@@ -61,7 +61,7 @@ public class ChordTable { | |
} | ||
} | ||
} | ||
print("generated \(returnChords.count) chords") | ||
//print("generated \(returnChords.count) chords") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment Spacing Violation: Prefer at least one space after slashes for comments. (comment_spacing) |
||
return returnChords | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,15 @@ class ChordTests: XCTestCase { | |
XCTAssertEqual(Chord.Asus2.description, "Asus2") | ||
XCTAssertEqual(Chord.Bsus2.description, "Bsus2") | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trailing Whitespace Violation: Lines should not have trailing whitespace. (trailing_whitespace) |
||
func testC7() { | ||
XCTAssertEqual(Chord(.C, type: .dominantSeventh).description, "C7") | ||
let notes: [Int8] = [60, 67, 70, 76] | ||
let c7 = PitchSet(pitches: notes.map { Pitch($0) } ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Closing Brace Spacing Violation: Closing brace with closing parenthesis should not have any whitespaces in the middle. (closing_brace) |
||
let chords = Chord.getRankedChords(from: c7) | ||
XCTAssertEqual(chords.map { $0.description }, ["C7"]) | ||
|
||
} | ||
|
||
func testRomanNumerals() { | ||
XCTAssertEqual(Key.C.primaryTriads.map { $0.romanNumeralNotation(in: Key.C) ?? "" }, | ||
|
@@ -64,7 +73,7 @@ class ChordTests: XCTestCase { | |
XCTAssertEqual(Cmaj7?.description, "Cmaj7") | ||
|
||
let ChalfDim7 = Chord(notes: [.C, .Eb, .Gb, .Bb]) | ||
XCTAssertEqual(ChalfDim7?.description, "C(1/2)°7") | ||
XCTAssertEqual(ChalfDim7?.description, "Cø7") | ||
|
||
let Adim7 = Chord(notes: [.A, .C, .Eb, .Gb]) | ||
XCTAssertEqual(Adim7?.description, "A°7") | ||
|
@@ -85,9 +94,9 @@ class ChordTests: XCTestCase { | |
let G11 = Chord(notes: [.G, .B, .D, .F, .A, .C]) | ||
XCTAssertEqual(G11?.description, "G11") | ||
|
||
let BhalfDiminished11NoteSet = NoteSet(notes: [.B, .D, .F, .A, .C, .E]) | ||
let BhalfDiminished11NoteSet = NoteSet(notes: [Note(.B, octave: 1), .D, .F, .A, .C, .E]) | ||
let chords = ChordTable.shared.getAllChordsForNoteSet(BhalfDiminished11NoteSet) | ||
XCTAssertTrue(chords.contains(where: { $0.description == "B(1/2)°11" })) | ||
XCTAssertTrue(chords.contains(where: { $0.description == "Bø11" })) | ||
} | ||
|
||
func testThirteenthNaming() { | ||
|
@@ -98,21 +107,27 @@ class ChordTests: XCTestCase { | |
XCTAssertTrue(chords.contains(where: { $0.description == "Em♭13♭9" })) | ||
XCTAssertTrue(chords.contains(where: { $0.description == "Fmaj13♯11" })) | ||
XCTAssertTrue(chords.contains(where: { $0.description == "Am11♭13" })) | ||
XCTAssertTrue(chords.contains(where: { $0.description == "B(1/2)°♭13" })) | ||
XCTAssertTrue(chords.contains(where: { $0.description == "Bø♭13" })) | ||
} | ||
|
||
func testInversions() { | ||
let chord = Chord(notes: [.C, .E, .G])! | ||
XCTAssertEqual(chord.inversion, 0) | ||
XCTAssertTrue(chord.isTriad) | ||
|
||
let firstInversion = Chord(notes: [.C, .E, Note(.A, octave: 6)])! | ||
var firstInversion = Chord(notes: [.C, .E, Note(.A, octave: 6)])! | ||
XCTAssertEqual(firstInversion.inversion, 1) | ||
XCTAssertEqual(firstInversion.description, "Am") | ||
XCTAssertEqual(firstInversion.slashDescription, "Am/C") | ||
|
||
let secondInversion = Chord(notes: [Note(.E, octave: 1), .A, .C])! | ||
XCTAssertEqual(secondInversion.inversion, 2) | ||
XCTAssertEqual(secondInversion.description, "Am") | ||
XCTAssertEqual(secondInversion.slashDescription, "Am/E") | ||
|
||
let thirdInversion = Chord(.C, type: .dominantSeventh, inversion: 3) | ||
XCTAssertEqual(thirdInversion.slashDescription, "C7/B♭") | ||
|
||
firstInversion = Chord(.Cs, type: .majorTriad, inversion: 1) | ||
XCTAssertEqual(firstInversion.slashDescription, "C♯/E♯") | ||
} | ||
|
||
func testTriadsWithRedundantNotes() { | ||
|
@@ -146,7 +161,7 @@ class ChordTests: XCTestCase { | |
let midiNotes: [Int8] = [54, 58, 61] | ||
let fSharp = PitchSet(pitches: midiNotes.map { Pitch($0) } ) | ||
let chords = Chord.getRankedChords(from: fSharp) | ||
XCTAssertEqual(chords.map { $0.description }, ["F♯", "G♭"]) | ||
XCTAssertEqual(chords.map { $0.description }, ["G♭","F♯"]) | ||
} | ||
|
||
func testDuplicateRankedChords() { | ||
|
@@ -259,13 +274,18 @@ class ChordTests: XCTestCase { | |
"Notes should match expected notes for 1st inversion" | ||
) | ||
} | ||
|
||
func testBassNoteChords() { | ||
// C Major 1st inversion | ||
let notes: [Int8] = [4, 7, 12] | ||
let pitchSet = PitchSet(pitches: notes.map { Pitch($0) }) | ||
let chords = Chord.getRankedChords(from: pitchSet) | ||
XCTAssertEqual(chords.map{$0.bassNote}, [NoteClass.E]) | ||
} | ||
|
||
func assertChords(_ notes: [Int8], _ expected: [Chord]) { | ||
let pitchSet = PitchSet(pitches: notes.map { Pitch($0) }) | ||
// print(pitchSet.array.map { Note(pitch: $0)}) | ||
let chords = Chord.getRankedChords(from: pitchSet) | ||
// print(chords, expected) | ||
// Note that this is strange that we can't compare the arrays directly | ||
XCTAssertEqual(chords.description, expected.description) | ||
} | ||
|
||
|
@@ -289,7 +309,7 @@ class ChordTests: XCTestCase { | |
assertChords([11, 15, 18], [.B, .Cb]) | ||
|
||
// Extensions that can be spelled only without double accidentals should be found | ||
assertChords([1, 5, 8, 11], [Chord(.Cs, type: .dominantSeventh), Chord(.Db, type: .dominantSeventh)]) | ||
assertChords([1, 5, 8, 11], [Chord(.Db, type: .dominantSeventh), Chord(.Cs, type: .dominantSeventh),]) | ||
assertChords([1, 5, 8, 11, 14], [Chord(.Cs, type: .flatNinth)]) | ||
|
||
} | ||
|
@@ -299,7 +319,6 @@ class ChordTests: XCTestCase { | |
let results: [Int8] = [60, 64, 67] | ||
let pitchSet = PitchSet(pitches: openNotes.map { Pitch($0) }) | ||
let resultSet = PitchSet(pitches: results.map { Pitch($0) }) | ||
print(pitchSet.closedVoicing.array) | ||
XCTAssertEqual(pitchSet.closedVoicing, resultSet) | ||
} | ||
|
||
|
@@ -308,7 +327,6 @@ class ChordTests: XCTestCase { | |
let results: [Int8] = [0, 4, 7] // another idea | ||
let pitchSet = PitchSet(pitches: openNotes.map { Pitch($0) }) | ||
let resultSet = PitchSet(pitches: results.map { Pitch($0) }) | ||
print(pitchSet.closedVoicing.transposedBassNoteTo(octave: -1).array) | ||
XCTAssertEqual(pitchSet.closedVoicing.transposedBassNoteTo(octave: -1), resultSet) | ||
} | ||
|
||
|
@@ -317,7 +335,6 @@ class ChordTests: XCTestCase { | |
let results: [Int8] = [0, 4 + 12, 7 + 24, 0 + 24, 4 + 36] // another idea | ||
let pitchSet = PitchSet(pitches: openNotes.map { Pitch($0) }) | ||
let resultSet = PitchSet(pitches: results.map { Pitch($0) }) | ||
print(pitchSet.transposedBassNoteTo(octave: -1).array) | ||
XCTAssertEqual(pitchSet.transposedBassNoteTo(octave: -1), resultSet) | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ final class PerformanceTests: XCTestCase { | |
index_sum += Note(pitch: Pitch(Int8(i))).intValue | ||
} | ||
if index_sum != 23779 { | ||
print("index_sum: \(index_sum)") | ||
//print("index_sum: \(index_sum)") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment Spacing Violation: Prefer at least one space after slashes for comments. (comment_spacing) |
||
abort() | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing Whitespace Violation: Lines should not have trailing whitespace. (trailing_whitespace)