implement glot algorithm to voxbox
task id: implement-glot2024-05-28 09:57: Hoping to start the glottis port today. #implement-glot
2024-05-28 15:16: glot pre-setup: import wavwrite #implement-glot
This is hard for me because I don't actually know what the best practice is for organizing library code.
Found this: https://rust-classes.com/chapter_4_3
2024-05-29 09:43: Some initial work on glottis scaffolding #implement-glot
2024-05-30 09:25: Didn't complete this yesterday. Completing today. #implement-glot
2024-05-30 09:32: Building glot initializer #implement-glot
Struct initialization feels like a pain point to me.
2024-05-30 09:55: HP/LP filter uses similar struct, how to do inheritance like things? #implement-glot
Using this as a learning opportunity.
Okay, looks like I want to implement a filter as a trait. See: <<rust/books/rust_by_example/traits>> and <<rust/books/rust_book/traits>>.
2024-05-30 10:12: Trying to get traits to work is premature optimization and too much time, making a new task for it. #implement-glot #refactor-glotfilt-traits
I want to set aside some time to actually do this right. It's not quite fitting in my head at the moment, and I need to keep moving forward. Ultimately, what we want is to be able to have a nice way to define two two types: highpass and lowpass, which are two types that use identical data structs but slightly different methods.
I imagine it'll be pretty trivial to return and refactor. So far, this has been my experience with Rust.
2024-05-30 10:16: Back to Butterworth porting #implement-glot
These are needed to filter the noise source in the glottal model.
2024-05-30 10:18: Oh wait, I implemented the butterworth filter in boing^3 #implement-glot
I'll just copy that over. duh.
2024-05-30 10:23: Ugh, how do I use modules locally? need a refresher. #implement-glot
Revisiting <<rust/organizing_code_project_structure>>.
Here is the winning code block for imports.
use crate::butterworth::{ButterworthLowPass, ButterworthHighPass};
Also needed to make sure that lib.rs had butterworth imported too.
2024-05-30 10:36: Back to filling out rest of the glot struct #implement-glot
I will need to return to implement the highpass filter.
2024-05-30 10:45: implement setup_waveform #implement-glot
2024-05-30 11:02: implement hanning table #implement-glot
2024-05-30 11:06: implement highpass #implement-glot
2024-05-30 11:09: initializer done. now some of the smaller methods #implement-glot
2024-05-30 11:15: Port tick function. #implement-glot
2024-05-30 11:30: I think there's enough in place for some initial sound #implement-glot
2024-05-30 11:37: Uh oh, it panicked. #implement-glot
There was an attempt to multiply with overflow. It seems to be a problem with the RNG.
disabling noise source for now.
2024-05-30 11:40: No sound. Debugging in my future. #implement-glot
2024-05-30 11:55: troubleshooting: why isn't there any sound? #implement-glot
Here is the debug script:
rm -f glot_simple.wav
cargo run --example glot_simple
mnolth wavdraw glot_simple.wav glot_simple.pbm
ah. I didn't finish setup_waveform()
2024-05-30 13:11: Line by line checking with reference #implement-glot
I will log as I go.
setup_waveform: found multiply that should have been add. otherwise, it looks okay.
settuptable looks good. -- rest of initializer looks good. -- tick(): I messed up most of the exp function translations. Rust has a weird notation for this. instead of exp(x)
, rust does x.exp()
.
2024-05-30 14:00: final thoughts #implement-glot
I have finished porting the glottal source component of my singing synthesizer from C to Rust. It was a bit of a tedious effort, but luckily the setbacks were small.
The port can be found here: https://github.com/PaulBatchelor/voxbox/blob/main/src/glot.rs.
Sample code writing to WAV file: https://github.com/PaulBatchelor/voxbox/blob/main/examples/glot_simple.rs.
This implements a glottal source model based on the one described by Hui-Ling Lu in their dissertation. It includes a version of the LF glottal flow derivative waveform model (based off an implementation by Neil Thapen of Pink Trombone), as well as a synchronous pulsed noise component for breathiness and aspiration.
On the first pass manually translating C to Rust, I ran into some issues. Most of the issues ended up being related to the way Rust handles math functions. Instead of doing something like exp(x)
like you'd expect, Rust tends to use notation like x.exp()
, and I messed up the notation.
By itself, the glottal source component sounds pretty unremarkable. It will need a tract filter before it starts to sound talky.
This is the algorithm I'd like to use: https://git.sr.ht/~pbatch/mnodes/tree/master/item/glot/glot.c.