Create "trio" demo

Create "trio" demo

task id: demo-trio

2024-07-12 08:58: This concept feels pretty fleshed out #concept-concerto #demo-trio

I think I might call it "trio", and the elevator pitch is that you control one voice in a trio, and there is an algorithm that controls the other two pitches to form 3 part harmony.

Follow-up task created: demo-trio.

2024-07-12 08:59: First step in trio: get instantaneous chords #demo-trio

2024-07-12 12:21: Initial boilerplate code, maybe some chords #demo-trio #timelog:01:33:13

2024-07-12 12:22: make copy of the singer test. #demo-trio

2024-07-12 12:36: getting the pitch control happening in rust instead of C #demo-trio

2024-07-12 12:51: time for initial chord logic? #demo-trio

For now: given a pitch (octave-wrapped, 0-11), find the corresponding triad. Use a lookup table (vector, probably).

2024-07-12 12:54: consolidate voice into struct #demo-trio

2024-07-12 13:05: two intial instances #demo-trio

2024-07-12 13:16: initial chord look-up table #demo-trio

2024-07-12 13:51: initial chord logic works! #demo-trio

I'm going to need to rework this to make it work with gesture and chords, but this is a good start.

2024-07-12 13:56: changing the smooth times makes a HUGE difference wow #demo-trio

2024-07-18 08:39: complete in theory. time to test in trio demo. #event-driven-gesture #demo-trio

Attempt voice staggering using event-driven gesture algorithm, see what goes wrong.

2024-07-18 12:35: Time to revisit the trio. #demo-trio #timelog:03:01:06

Now that I supposedly have an event-driven gesture, I need to begin work incorporating those mechanics into the existing demo.

2024-07-18 12:36: refamiliarizing myself with the work. #demo-trio

2024-07-18 13:00: light refactoring. main clock added. #demo-trio

Now, for the initial process of introducing these eventful gestures.

Before any events can be added to any gestures, we need to be able to simulate adding events. Events should only be added after the lead has held onto a pitch for one period.

The easiest way to do this using a global timer that keeps track the phasor ticks, which occur once a second. If a u32 is used, how many seconds is that?

Okay, so that's about a millenium short of 50k years. I think there's no general risk of things overflowing. I will be long dead and forgotten before that happens to a potential user.

Is this enough granularity though if I want to detect pitch changes? Maybe not actually.

2024-07-18 13:11: How to tell if pitch has lasted for a period. #demo-trio

Might need to ink this one out.

2024-07-18 13:27: Inked out an idea #demo-trio

First, implement things so that it detects changes in pitch. Then, build a sample and hold with along with a global monotonically increasing clock that increments at the start of every tick.

Now, any time a pitch change occurs in the lead, it can be compared against the sampled pitch. If the pitch is different, but the timestamps aren't, the whole period is marked as having been changed for the rest of the period.

The "dirty bit" trick is used in situations where the pitch changes quickly inside a period.

2024-07-18 13:32: Implementing pitch changes, alert on change. #demo-trio

2024-07-18 13:37: implementing clock and sample and hold #demo-trio

2024-07-18 13:50: shoot. I'm actually unable to articulate when a change *should* happen #demo-trio

I think there may be another state required.

How about this: If there has been a requested pitch change from the current lead pitch, and this request has been held for a period, apply this changed pitch to the current lead.

This should happen at the start of every period.

2024-07-18 14:05: well, now my logic isn't working at all. #demo-trio

I was setting things to be negative oops.

2024-07-18 14:11: this timing logic is trickier than I expected #demo-trio

2024-07-18 14:15: Going to try caching the lead value between takes. #demo-trio

Caching the lead value and also storing the last changed value may be what I'm looking for.

Essentially, this lead value needs ot be updated regardeless, but the other two voices need to be delayed.

2024-07-18 14:19: Okay, the complexity comes from the lead value needing to be updated #demo-trio

I can't simply cache the last lead pitch every time because I'm setting it every time.

2024-07-18 14:23: The delayed behavior works as expected now. #demo-trio

Now I am looking to delay chord changes on the upper/lower voices.

2024-07-18 14:28: I think I might be able to work staggering without gesture #demo-trio

I think I'm going to have to do this anyways because the event scheduler in the eventful gesture is so small, and can really only take one vertice at a time.

2024-07-18 14:34: reworking things for variable stagger times #demo-trio

The logic would need to be: lower gets set first, then upper.

Initially I had it so that the chord would be determined, and then you'd send off the events with delays. Rethinking this a bit.

I want it so that the upper pitch only changes if it waits 2 beats, and the lower pitch changes if it waits 1 beat.

2024-07-18 14:48: I'm going to try having another state variable #demo-trio

I want to have a state when the lower changed, but the upper hasn't. When the upper gets changed, the lower changed gets setup.

2024-07-18 15:01: Now I want the voices to be staggered on too #demo-trio

2024-07-18 15:08: Weird timing bugs #demo-trio

2024-07-18 15:14: Try to reset clock on new voice. #demo-trio

2024-07-18 15:34: Clock works, but the voices aren't resetting pitches #demo-trio

This causes some very rude sounding glisses sometimes.

2024-07-18 15:45: it works. clocking out. #demo-trio

2024-07-19 08:19: Clock resets are going to throw off gesture algorithm #demo-trio

One of the things I am doing in Trio is forcing a phase reset on pen down so the voices always stagger the same way. The funderlying rephasor in the gesture signal generator does not have a way of handling phase resets, so it'll assume a faster tempo change and cause unwanted jumps in time. The rephasor needs a way to be told that there has been a phase reset, which would cause it to skip calculating the delta time in the next sample tick.

I wonder if we can build a test around this?

2024-07-19 08:26: Task created for gesture reset #gesture-reset #demo-trio

I want to not only do the fix, but create a test in the test suite that showcases the problem before the fix is created, just so I know that I properly understand the problem that I am trying to solve.

2024-07-19 14:31: Time to actually hook up gesture to trio #demo-trio #timelog:00:36:25

2024-07-19 15:04: Things sound better than expected. #demo-trio

It did not crash. Not one crash. Thanks, Rust.

2024-07-19 15:07: A quick break. #demo-trio

2024-07-19 15:25: More trio tweaks #demo-trio #timelog:00:50:22

2024-07-19 15:26: Need a way to instantaneously set value of gesture. #demo-trio

This can be like a method or something like immediate(). Setting the next/prev values to be some scalar value should work for the most part. The event queue should also be cleared as well.

2024-07-19 15:53: Weird bug: playing the same note twice doesn't trigger events #demo-trio

2024-07-19 16:04: A rewrite might be in order for this voice state management #demo-trio

There's too much spaghetti right now. I think I need to actually attempt to model this as a state machine and add some tests. Otherwise, I'm never going to figure out the issue, and this code will become too unwieldy to maintain.

2024-07-19 16:13: Gesture is unpredictable. #demo-trio

Not sure if this is my scheduling logic, or some subtle bug with the gesture event queue, or a mix of both.

Testing is definitely in order. But like, not today. I'm pretty drained.

Yeah, considering I want to change the chord selection logic, it's going to be even worse. This needs a rewrite.

I have built what is essentially a concurrent event system. So yeah, this is tough one to do correctly.

2024-07-20 21:00: Some impromptu showings of trio demo prototype #demo-trio

I turned on gesture again, and I think the wrong notes are caused by the event graph not removing events at the right time. I think. Anyways, this needs to be rebuilt for sure because I don't want to live life doing guess and check.

2024-07-22 09:03: good time to figure out pikchr? #demo-trio

I'm trying to more formally establish a way to manage how the voice scheduling works for trio.

2024-07-24 10:11: A more sophisticated chord selection algorithm is required #demo-trio-chords #demo-trio

The issue is there is no sense of resolution anywhere. It still kinda meanders.

2024-07-25 15:53: Initial chord structure, now to think about querying #demo-trio

We want to make a query: given some pitch (0-11), and a current chord, provide a range of potential candidates for the next chord.

Since this is going to be used in a realtime situation. My thinking is to populate with a pre-allocated fixed size array that is managed internally.

2024-07-28 20:04: More reverb, predelay too. #demo-trio #timelog:00:14:42

2024-07-28 21:02: Some expression control #demo-trio #timelog:00:43:36

2024-07-29 09:40: How does this sound today? #demo-trio #timelog:00:40:00

Added a new shape. Really expressive now. Vibrato not as bad as I remembered (with headphones on now instead of speakers).

Should resolve to tonic more often. There should be a built in bias to choose tonic.

I'm desiring the instantaneous mode again.

The chord changes are happening too quickly. If I select a pitch, go down quickly, and og back up, the chord shouldn't change. But it does.

2024-07-29 10:11: Sounding chords are different from hypothetical ones #demo-trio

Right now, a changing pitch triggers a set of chord progressions. But if they move too quickly, only the last one is heard. Which means effectively an arbitrary chord transition.

How to prevent this? A chord isn't a chord until all 3 voices commit. If the lead changes pitch, it uses the stored chord as a reference point. As it continues to change, this chord will continue to be used. The upper voice is the last person to commit to a chord, so only then should the reference chord be updated.

When the chord is officially committed for selection, only then does that chord get registered in the chord frequency table.

Oh my god this is consensus in a distributed system.

2024-07-29 14:30: Working out the "chord committing" idea on paper. #demo-trio #timelog:00:40:08

2024-07-31 09:52: Tweaked the tract lengths too #demo-trio

2024-07-31 09:55: revisit mobile, add smoothing, and other things. publish #demo-trio #timelog:01:15:28

2024-07-31 10:02: shoot I am having too much fun with this thing it's working it's really working #demo-trio

That's how I know this is close.

2024-07-31 10:04: Okay for real, smoothing. #demo-trio

2024-07-31 10:18: My phone is almost definitely running at a higher sampling rate #demo-trio

I want to see if I can get the webaudio API to request a sampling rate (44.1kHz) because the voices are just going to sound better.

I am now coming to understand why gnuspeech decided to go the route of resampling from 16kHz to the host sampling rate. It's just going to sound better that way. The changes sound too weird.

2024-07-31 10:46: Getting caught up in tweaking the "ooo" vocal shape #demo-trio

I'm finding it's very hard to find in my interface, because the sliders end up being very close to zero. I almost feel the urge to have a min/max slider too to be able to "zoom in".

2024-07-31 11:04: Reqeusted sample rate of 44.khz. Sounds better now #demo-trio

2024-07-31 11:08: Time to upload this thing. #demo-trio

2024-07-31 11:14: Uploaded. #demo-trio

2024-07-31 12:55: Safari troubleshooting. #demo-trio #timelog:00:11:13