Implement chord selection algorithm
task id: demo-trio-chords2024-07-24 10:11: A more sophisticated chord selection algorithm is required #demo-trio-chords #demo-trio
2024-07-25 12:54: Trio Chord Selector planning #demo-trio-chords #timelog:01:11:47
2024-07-25 14:41: Plan out some initial code #demo-trio-chords #timelog:01:16:26
The initial structures: chords, and valid chord transitions.
2024-07-26 09:51: Implement query. #demo-trio-chords #timelog:00:59:53
Query: given a current chord, and a new lead note, find all the valid chords to transition to.
2024-07-26 12:57: transition tracker #demo-trio-chords #timelog:02:41:39
2024-07-26 14:06: Something is wrong with the query. #demo-trio-chords
I am trying to transition D4 in the key of C. This should give me two chords (G and Dm). But I'm getting no chords.
Ah, my "RE" constant was miscoded. Phew.
2024-07-26 14:13: Need a way to remove candidate chords in place #demo-trio-chords
Chords need to be removed.
2024-07-26 14:23: I'm running into overflow issues. Why? #demo-trio-chords
Oh just figured it out. I was using "chord index" instead of "index".
2024-07-26 15:07: Initial test created for note transition chord test #demo-trio-chords
This has an interface that applies the heuristic to the candidate, which effectively removes the previous chord in place.
2024-07-26 15:12: kind of a clunky interface, but it can be reworked later #demo-trio-chords
I'm querying the chord, removeing the previous transition chord, and updating the note pair table in 3 separate steps. It feels maybe too low-level and could be consolidated better?
2024-07-26 15:22: remove previous transition not working as expected #demo-trio-chords
It has no effect at all apparently.
2024-07-26 15:34: I think I found an off by one error #demo-trio-chords
I'm 99% sure it's the problem, but is it worth the problem?
There are two kinds of references right now: zero-index and one-indexed. The one-indexed ones are used because it means 0 can be reserved for None. was_used_last
expects a zero-indexed chord. But really, these indexes are transparent for the most part. add_chord
returns a zero-indexed position as well.
I think it makes sense to 1-index everything because that 0 is so critical.
2024-07-26 15:52: Fixed indexing #demo-trio-chords
Wow, that was tedious. Glad I had the tests. It almost makes me wished I used Option
...
2024-07-26 15:56: What happens if the current note doesn't have a valid state transition? #demo-trio-chords
There is no way to account for this yet. It will catch it as having zero candidates though.
2024-07-26 15:58: I'm now realizing that my look-up table should just be 12x12, not 12x11 #demo-trio-chords
Because it's harder to calculate what that second index is going to be. There also needs to be a check to make sure prev/next is not equal.
2024-07-27 20:18: implementing fallback chords #demo-trio-chords #timelog:00:17:27
It occured to me yesterday that I am actually close to a proof-of-concept system. I already have one heuristic working. I don't need to build out the others yet.
I do need to build a fallback set of chords in case a chord isn't found for a particular note.
2024-07-27 20:44: Figuring out how to actually hook this up #demo-trio-chords #timelog:00:28:53
2024-07-27 21:42: Find upper/lower voices of chord... how to? #demo-trio-chords
Instead of a lookup table, dynamically find the closest voices above and below a lead note given a chord.
2024-07-27 21:52: Note to self: chord select needs to reset on note off some how #demo-trio-chords
2024-07-27 21:57: Initial tests made. That's all I can do. #demo-trio-chords
I am incredibly exhausted today. What a long day.
2024-07-28 11:00: Implement upper/lower voice finder. #demo-trio-chords #timelog:00:09:43
2024-07-28 11:11: Plan out how to insert chords #demo-trio-chords #timelog:00:34:42
2024-07-28 11:41: I have built an initial "Chord Manager" #demo-trio-chords
I believe I have identified the entry points for the chord manager in the existing code, and I have built an initial dummy interface for it. There is a failing test now with the methods.
If I can implement this interface, I think it'll be ready for actual integration.
2024-07-28 11:47: Implementing chord manager #demo-trio-chords #timelog:00:49:00
2024-07-28 11:49: Populating the chord table. #demo-trio-chords
2024-07-28 12:22: Upper/lower movement could be different #demo-trio-chords
Upper/lower voice selection is based on minimal distance from lead voice. This could also change to be based on the minimal distance from the voice it is currently at while also being above/below the lead.
Writing this down, I'm realizing there's potential for chaos (an upper voice that somehow keeps drifting upwards, for instance). Could be a cool thing though.
2024-07-28 12:36: tests seem to pass for the initial chord manager #demo-trio-chords
2024-07-28 12:37: Let's see if I can make this work #demo-trio-chords #timelog:00:17:39
2024-07-28 12:56: I want a minor VI in there #demo-trio-chords #timelog:00:20:00
2024-07-28 13:03: Harmonic continuinity is coming through, but better voice leading needed #demo-trio-chords
Also running into a bug with chord harmonies. I get wrong chords. If I keep tapping on Do, I'll land on sustained chords, which aren't correct.
2024-07-28 13:07: Oh right, I think I need to make sure duplicates are ignored #demo-trio-chords
Still not fixing the bug I described above. The lower note is incorrectly holding onto the note of the previous chord for some reason.
2024-07-28 13:16: Pulling myself away. #demo-trio-chords
2024-07-28 14:49: Fixing the weird initial chord bug #demo-trio-chords #timelog:00:07:32
Retriggering the lowest note causes it to alternate between a major chord (correct) and a sustained chord (incorrect) The upper note is correct, while the lower note is incorrect.
2024-07-28 14:59: It never chooses anything but the top 2 right now #demo-trio-chords
2024-07-28 15:06: Minimal movement, and better choices #demo-trio-chords #timelog:00:19:50
The state transitions aren't shuffling through the states as well as I'd like. Also, there's too much unnecessary voice movement.
How can the system choose interesting chords and also minimize movement?
Specifically: I want the system to be introduced to the minor 4 (without resorting to randomness), and I want the voices to resolve in an expected way.
It is a constraint problem. Each voice has different behavior.
Upper voice should move as little as possible while still being above the lead voice.
Lower voice should move the minimum number of steps required to complete the chord, based on what the current upper and lead pitches are.
New heuristic idea: of the candidates, choose the least used chord so far. Probably keep a running heat map. This solves the chord selection problem, but not the voice leading issue
Another heuristic for chord selection is to find a candidate chord that requires the least amount of movement, given the current upper/lower pitches. This would require storing the current pitches of upper/lower, calculating the upper/lower pitches for each chord, and getting the combined step change for each chord. The one with the lowest score wins.
These are two different heuristics for selection, and I can imagine there would be a way to choose one over the other.
2024-07-28 15:27: Implement the "least used chord" heuristic #demo-trio-chords #timelog:00:58:01
2024-07-28 16:24: Heuristic implemented in tests, now to try it out #demo-trio-chords #timelog:00:09:35
2024-07-28 16:36: Least Movement Heuristic thoughts, test building #demo-trio-chords
Least movement: for each candidates, get the working upper and lower notes of those chords given the current lead pitch. Get the absolute differences between those and the current lower/upper. The one with the lowest score wins.
A good hybrid could be something called "Lazy Least Used". Go for the least used, but only if the movement required is under a certain score. Otherwise, choose the one with least movement
2024-07-28 16:42: Working out a least movement test. #demo-trio-chords #timelog:00:18:34
Firstly, make sure the low-level chord measurement mechanics are in place and working properly. Then, simulate a chord change.
2024-07-28 16:57: Implement measure movement core logic #demo-trio-chords #timelog:00:04:33
2024-07-28 17:02: Work out least movement heuristic test #demo-trio-chords #timelog:00:23:06
2024-07-28 17:10: Staggered voices complicate things #demo-trio-chords
You need to store what the pitch actually is somewhat reliably. There isn't currently a caching mechanism in place for this.
The entry points for this happen when a pitch is actually scheduled.
2024-07-28 17:20: What happens if you schedule a note that is the same? #demo-trio-chords
Minimal movement means lots of situations where a note stays the same.
Shouldn't really matter, for now. It'll cause more complication if a check is introduced (for example, handle what happens if there is no previous note).
2024-07-28 17:26: Implement least movement heuristic #demo-trio-chords #timelog:00:23:20
2024-07-28 17:50: Hook it up into the system #demo-trio-chords #timelog:00:19:03
2024-07-28 18:05: why is it crashing now? #demo-trio-chords
2024-07-28 18:21: It works! Next steps #demo-trio-chords
Lazy Least Frequent Heuristic, Y-axis expression.
Nice thing to have: some way to go from staggered to instantaneous behavior.
2024-07-28 18:26: LeastMovement crashes due to unwrap still #demo-trio-chords #timelog:00:06:33
Fixed.
2024-07-28 19:47: Attemping the Lazy Least Frequent Heuristic #demo-trio-chords #timelog:00:14:43
This one seems difficult to make a test for. I am tempted to just implemented it without tests, as the amount of effort it would take to find the perfect test situation wouldn't be worth it.
2024-07-28 20:03: It appears to be working. #demo-trio-chords
This is a good stopping point for my chords engine.
2024-07-29 15:16: Considering this task done #demo-trio-chords
Even though I still want to tweak the chord selection algorithm, the core work is done.
The issue is there is no sense of resolution anywhere. It still kinda meanders.