implement velum support in voxbox

implement velum support in voxbox

task id: implement-velum

2024-06-03 09:54: velum initial setup #implement-velum

Velum is not implemented in tubular <<voxbox/tubular>>, but it is in implemented in tract <<voxbox/tract>>. Getting tract code tangled and re-examine what needs to be added.

2024-06-03 10:12: core issue: how to set size of nose procedurally #implement-velum

The original tract waveguide had a fixed size of 28, which should be a size in CM (which will most likely be expressed as proptional percentage of the tract width in practice).

2024-06-03 10:18: figure out current size of nose in CM #implement-velum

The harddcoded size is 28, which I believe is 2x oversampled, so that's 14 samples.

Looks like it is around 10-12cm:

To derive, I opened the equation I was using to convert tract len (cm) to samples in bc:

(len * 0.01)/(speed_of_sound / sr)

or

(16 * 0.01)/(343.0 / 44100.0)

2024-06-03 12:00: gamejam kickoff (put velum on hold) #game-jam #implement-velum

I forgot this week was the game jam. My velum work will have to wait a few days.

2024-06-06 10:27: Where did I leave off with velum #implement-velum

2024-06-06 11:05: Try to add some scaffolding #implement-velum

2024-06-06 11:09: Actually realizing that "nose" is a better name #implement-velum

"Velum" refers to the membrane separating the oral cavity (mouth) from the nasal cavity (nose). The nose is another waveguide. The velum is just a value that controls the opening of this waveguide.

2024-06-06 11:29: Some initial scaffolding and comments. #implement-velum

I think I understand enough to start coding some things up.

2024-06-06 14:02: Back at home, coding up some nose/velum stuff. #implement-velum

2024-06-06 15:11: Back to working on this at home. #implement-velum

2024-06-06 15:21: I forgot about reflections #implement-velum

Things start to get trickier here beacuse I'm trying to separate tract and nose code, but this needs stuff from tract.

2024-06-07 13:06: nose tick function porting: right junction equation looks wrong #implement-velum

Above it is the computation for the left junction. I'd imagine a symmetry between them, but it is not symmetrical. The original voc source code looks like this as well. I think I may have to go back to Pink trombone and check on it. At some point. In the meantime, I've dropped a TODO.

2024-06-07 13:32: Hooking nose with tract in tick function #implement-velum

2024-06-07 13:49: Try to an initial example up and running. #implement-velum

2024-06-07 13:52: Uh-oh. Overflow with the existing example. #implement-velum

It's the damn LCG again in the glottis.

2024-06-07 14:01: tract example sounds chipmunky now #implement-velum

going off track a little bit

2024-06-07 14:20: Got a nice throat-singing patch going. No nose yet. #implement-velum

But it sounds cool. Might want to import my reverb for added effect.

2024-06-07 14:26: Okay it blew up. Fizzled into high frequency. #implement-velum

But the good news is that it did build.

2024-06-07 14:27: This is a good enough stopping point #implement-velum

I gotta get ready for a presentation.

2024-06-07 15:38: Debugging what went wrong #implement-velum

Line by line debugging, comparing to reference C file. Yick.

2024-06-07 16:18: Pretty sure the nose.tick() function is the culprit #implement-velum

looking at it again. I have tried to clean up the code to make it more readable.

2024-06-07 16:27: Nasal is a NaN! #implement-velum

Must be dividing by zero somewhere.

2024-06-07 16:32: NaN checking: no nans in nose reflections #implement-velum

2024-06-07 16:34: NaN checking: NaN introduced during scattering junctions #implement-velum

ns_r and ns_l both have it (which makes sense). Not sure which one gets it first. Going to check above.

2024-06-07 16:42: Finding the earliest NaN #implement-velum

Listing these as I go.

1367: tr_jr[nose_start].

1367: tr_l[nose_start] is NaN before tr_jr[nose_start].

2024-06-07 16:53: Taking a break from NaNs #implement-velum

NaN-hunting fries my brain.

2024-06-07 16:59: NanHunting: A little more digging #implement-velum

So far, I believe tr_l[nose_start] to be the earliest introduction of a NaN as sample position 1367.

2024-06-07 17:07: NaN introduced into tract left 1 sample earlier, after nasal #implement-velum

tr_l[nose_start] has a NaN at 1366 at the end of the computation loop, after the nasal component is made. It happens right after update_waveguide.

2024-06-07 17:09: Following update_waveguide #implement-velum

tr_l is copying over tr_jl with a multiply by 0.999. Where is the last place junction left is begin updated?

2024-06-07 17:11: Following last update of junction left #implement-velum

2024-06-07 17:17: I have lost the trail. Will start again tomorrow #implement-velum

2024-06-07 17:20: Final NaN thoughts #implement-velum

Junction Left at the nose start is the earliest instance of a NaN I have found in my code. At 1366, it is introduced before the nose tick, but after compute scattering junctions.

2024-06-08 11:29: The NaN hunt resumes, where was I? #implement-velum

2024-06-08 11:34: Earliear nan found at position 1365 #implement-velum

There is something wrong with the omega (w) coefficient being computed inside of compute_scattering_junctions. Peeling that back some more now.

w_r[i - 1] is fine, and doesn't NaN before w.

w_l[i] is also fine, and doesn't NaN out before w. r[i] is the only variable left in there. Let's see if it NaNs.

r[i] is also fine. huh?

2024-06-08 11:38: none of the components for =w= have NaNs #implement-velum

I'm guessing it must be an overflow NaN. Some big numbers somewhere. Going to use dbg! to inspect the variables w_r[i - 1], w_l[i], and r[i].

2024-06-08 11:42: =w_r[i]= and =w_r[i -1]= are both big numbers #implement-velum

Check it out:

[src/tract.rs:183:17] w_r[i - 1] = 2.5360622e38
[src/tract.rs:183:17] w_l[i] = 1.0904602e38
[src/tract.rs:183:17] r[i] = 0.0

So, I gotta figure out why these numbers are getting so large. In my experience, it's usually due to numbers being so small.

I could change the precision to f64 instead of f32, but this moves the goal posts, and might not actually fix the underlying problem.

2024-06-08 11:46: Where would =w_r= and =w_l= be getting such large numbers? #implement-velum

2024-06-08 11:57: Multiplying an inf by zero gives you a NaN #implement-velum

Found an intersting thing here. I wanted to break down the components giving me NaN. So I wrote some debugging code that looked like this:

    let add1 = w_r[i - 1] + w_l[i];
    let add2 = r[i] * add1;
    if add2.is_nan() {
        dbg!(r[i], add1);
        panic!("NAN");
    }

The results of this are here:

[src/tract.rs:190:17] r[i] = 0.0
[src/tract.rs:190:17] add1 = inf
thread 'main' panicked at src/tract.rs:191:17:
NAN

2024-06-08 12:12: Note to self: =is_finite()= is how to check for infinity checks #implement-velum

If it's false, it's inf.

2024-06-08 21:12: Some (hopefully) quick NaNhunting to try and move this a bit forward #implement-velum

2024-06-08 21:14: Find when =w_r[i - 1]= and =w_l[i]= become inf earlier in the code #implement-velum

It's going to be in the nose tick function somewhere I think.

Specifically, i is at position 11.

2024-06-08 21:31: Large numbers start happening early on #implement-velum

Specifically at tr_jr[nose_start].

2024-06-08 21:41: Tomorrow's strategy: comparing the numbers with the C implementation #implement-velum

It seems like the reflection coefficients might be causing things to blow up.

2024-06-09 10:32: Compare nose reflections with tract implementation. #implement-velum

2024-06-09 10:41: Left Reflection coefficient seems too high #implement-velum

In the rust version, I'm getting 2.0. In the original, it's -0.1.

2024-06-09 10:43: wait wait wait, I need to look at right reflection not left #implement-velum

Similar deal actually. The original is still a small negative value.

2024-06-09 10:46: Changed velum to 0.0 in reference, similar results #implement-velum

I have my velum set to be 0 in the rust version. So there is definitely something wrong with these left/right reflection coefficient calculations.

2024-06-09 10:52: Sum value is very different #implement-velum

It is larger in the reference (~7.3) vs my implementation (1.062).

I had my nose starting position calculated using a relative percentage of the tract length. Maybe this is an unstable way to do this?

2024-06-09 10:59: I think I found it. #implement-velum

There were typos in my reflection calculations. Additions that were supposed to be subtractions. Looking up the wrong index.

2024-06-09 11:01: We have actual sound! #implement-velum

2024-06-09 11:02: Velum control seems to work! #implement-velum

2024-06-09 11:03: Reworking demo #implement-velum

I lost the ideal tube shape sound. Going to need to fiddle around with the interactive example.

2024-06-09 11:24: Getting velum slider working: Unknown upcode (195) #implement-velum

2024-06-09 11:32: commented out panics and debug prints #implement-velum

I think this was getting in the way of wasm compilation.

2024-06-09 11:48: turning off vibrato #implement-velum

2024-06-09 12:50: Some initial throat singing shapes made #implement-velum

lots and lots of trial and error

2024-06-09 13:33: Smooth transitions between overtones #implement-velum

At this point, I'm just having fun. I think the velum is correct haha

2024-06-09 14:06: Working on a melodic sequence #implement-velum

2024-06-09 14:15: Drown it in reverb #implement-velum

2024-06-09 14:42: Throat singing demo works, created mp3 for it. #implement-velum

Now, time for cleanup.

2024-06-09 14:57: well, it's a good thing I made an mp3... #implement-velum

My shapes for the throat singing example are ruined once again because there was some code I accidentally commented out.

2024-06-09 15:46: Another attempt #implement-velum

2024-06-09 16:09: probably good enough #implement-velum

2024-06-09 16:13: restoring web example, now with velum #implement-velum