Tonic icon indicating copy to clipboard operation
Tonic copied to clipboard

Lazy Chord loading on Key Type

Open SebastianBoldt opened this issue 5 months ago • 1 comments

Description

From what I understand, when you initialize a Key, all the chords are generated inside the init. However, if you want to create all the available keys for a certain note, the performance is pretty bad.

Here is a piece of code that illustrates this concept:

let newKeys = Scale.allCases.map {
    Key(root: note, scale: $0)
}

This code creates an array of keys for all the scales of a given note.

Proposed Solution

I would suggest implementing lazy chord creation. This would allow users to request the chords for a specific key only when needed, instead of generating them automatically during initialization. I believe that generating chords during initialization is unnecessarily complex and pollutes the domain of the Key Object, in addition, it also impacts the performance.

/// All the traditional triads representable root, third, and fifth from each note in the key
public func primaryTriads() ->[Chord] {
    let table = ChordTable.shared
    var primaryTriads: [Chord] = []
    let allowablePrimaryTriads: [ChordType] = [.majorTriad, .minorTriad, .diminishedTriad, .augmentedTriad]

    for (_, chord) in table.chords where chord.noteClassSet.isSubset(of: noteSet.noteClassSet) {
        if allowablePrimaryTriads.contains(chord.type) {
            primaryTriads.append(Chord(chord.root, type: chord.type))
        }
    }

    let primaryTriadsStartingWithC = primaryTriads.sorted(by: { $0.root.letter < $1.root.letter })
    let rootPosition = primaryTriadsStartingWithC.firstIndex(where: { $0.root == root }) ?? 0
    return Array(primaryTriadsStartingWithC.rotatingLeft(positions: rootPosition))
}

/// All chords that fit in the key
public func chords() -> [Chord] {
    let table = ChordTable.shared
    var chords: [Chord] = []
    for (_, chord) in table.chords where chord.noteClassSet.isSubset(of: noteSet.noteClassSet) {
        chords.append(Chord(chord.root, type: chord.type))
    }
    return chords
}

We could also think about adding a static global cache, similar to Chordtable, that stores a key and all the corresponding chords globally for faster access.

Wdyt?

SebastianBoldt avatar Jan 26 '24 13:01 SebastianBoldt

These sound like good solutions. Thank you for pushing on this.

aure avatar Jan 26 '24 16:01 aure