I asked this question on Stack Overflow and was directed here. I'm working on a function that will help me quickly find all the upper structure triads (3-note chord) that I can add to a 4-note 7th chord to create a larger compound chord, as well of the roots and names of each triad. The example I'm testing right now is a 13#11 chord, which has the following degrees in a 12-note octave (when the root is 0): [0, 2, 4, 6, 7, 9, 10]
*
*side note: [0, 4, 6, 7, 9, 10]
would also match as a 13#11.
The base 7th chord is a dominant 7th: [0, 4, 7, 10]
.I already know which triads complete the chord: a major triad on the 9th, [2, 6, 9]
, and a diminished triad on the #11, 0, 6, 9
(only the 6
(#11th) and 9
(13th) are actually necessary to build a 13#11 chord; the 2
(9th) is optional).
I actually already have a function that will give me these results, I'm just wondering if there's a faster/more efficient way to do it? It just feels a little bulky/clunky right now.
On Stack Overflow @RobNapier suggested that I look at using Enums instead of String, [String], Int, and [Int]. I definitely get how Enums would help for my triadQualities
array. I'm not sure what they would improve with the number combinations. Or what other improvements (enum-related or other) I can make. Can anyone help enlighten me? Any help would be appreciated.
the code
extension Int {
func degreeInOctave() -> Int {
switch self {
case 0...11:
return self
case 12...:
return self - 12
default:
return self + 12
}
}
}
var ust: [Int] = [0, 2, 4, 6, 7, 9, 10]
let maj = [0, 4, 7]
let min = [0, 3, 7]
let aug = [0, 4, 8]
let dim = [0, 3, 6]
let sus4 = [0, 5, 7]
let sus2 = [0, 2, 7]
let triadDegs = [maj, min, aug, dim, sus4, sus2]
var triadRoots: [Int] = []
var triadQualities: [String] = []
func findUpperStructureTriads(degs: [Int]) {
let degCount = degs.count
var firstIndex = 0
while firstIndex < (degCount - 2) {
var secondIndex = firstIndex + 1
while secondIndex < (degCount - 1) {
var thirdIndex = secondIndex + 1
while thirdIndex < (degCount) {
var threeNoteGroup = [degs[firstIndex], degs[secondIndex], degs[thirdIndex]]
func checkForTriad(triad: [Int]) -> Bool {
if triadDegs.contains(triad) {
switch triad {
case maj:
triadQualities.append("major")
case min:
triadQualities.append("minor")
case aug:
triadQualities.append("augmented")
case dim:
triadQualities.append("diminished")
case sus4:
triadQualities.append("sus4")
case sus2:
triadQualities.append("sus2")
default:
()
}
return true
} else {
return false
}
}
if threeNoteGroup.contains(6), threeNoteGroup.contains(9){
var inversionCount = 0
var newGroup = threeNoteGroup.map {$0 - threeNoteGroup[0]}
while inversionCount < 3 {
func invert() {
newGroup = newGroup.map {($0 - newGroup[1]).degreeInOctave()}
let newlast = newGroup.remove(at: 0)
newGroup.append(newlast)
}
if checkForTriad(triad: newGroup) {
print(threeNoteGroup, threeNoteGroup[inversionCount])
triadRoots.append(threeNoteGroup[inversionCount])
break
}
invert()
inversionCount += 1
}
}
thirdIndex += 1
}
secondIndex += 1
}
firstIndex += 1
}
for i in 0...(triadRoots.count - 1) {
print(triadRoots[i], triadQualities[i])
}
}
findUpperStructureTriads(degs: ust)
outputs
[0, 6, 9] 6
[2, 6, 9] 2
6 diminished
2 major
clarifications
The combined ust
chord is always sorted from lowest to highest. Its elements can range between 0 and 11, and they never duplicate. The lower 4-note chord and upper 3-note chord get merged into one larger chord, lower chord first. If any number in the upper chord is already in the combined chord it doesn't get added twice. So if the lower chord is [0, 4, 7, 10]
and the upper chord is [2, 7, 10]
, ust
would be [0, 2, 4, 7, 10]
. And for that chord the outputs of the combinations function (ideally) would be something like:
[0, 2, 7] 0
[2, 7, 10] 2
0 sus2
7 minor
Note: One thing I realized is that this function may not actually help my larger program—it would replace a function I'm already using that takes hard-coded Int arrays for every combined chord degree array and translates them into string formulas (i.e. [1, 2] = "min triad on the Maj 2nd"). So I've already done the work. And for the new function to work I'd need to figure out an if statement for which degrees are required for completion that changed for every possible chord quality. So this may all be moot
ust = [0, 2, 4, 7, 10]
not working is fixable by changing this if statementif threeNoteGroup.contains(6), threeNoteGroup.contains(9)
. But I'm not asking about that part (because I know how to fix it). I'm more asking whether the way I'm iterating through the combinations can be improved to be faster and/or take up fewer lines of code. \$\endgroup\$