implement velum support in voxbox
task id: implement-velum2024-06-03 09:54: velum initial setup #implement-velum
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
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.