-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pre-warm WaveNet on creation over the size of the receptive field #71
Conversation
…Removed no longer needed anti-pop code.
Punchline first: I'd recommend that if you've got a project that needs this, that project should define its own function to handle it like (copying your code...) void warmup(DSP* model) {
long receptive_field = model->get_receptive_field(); // Looks like this needs to be defined but not unreasonable to do so I think.
NAM_SAMPLE sample = 0;
NAM_SAMPLE* sample_ptr = &sample;
std::unordered_map<std::string, double> param_dict = {};
for (long i = 0; i < receptive_field; i++)
{
model->process(&sample_ptr, &sample_ptr, 1, 1, 1.0, 1.0, param_dict);
model->finalize_(1);
sample = 0;
}
} The reason why is that this won't work for all of the models that are currently supported by this library (specifically, parametric models). But also, I'm just not confident that it should be enshrined as "the way" to do this--like you said, this would incur a processing spike as the new net gets up to speed while an existing net continues to process samples in the RT loop. Since CPU usage is a priority for some folks, I don't want to box them out. Other high-level comments:
A few lower-level comments:
Mmh, not necessarily--some of the element-wise calculations like the activation should be able to be vectorized (putting aside for a moment how well that's done right now). WaveNet is different from an LSTM in that way--you can vectorize the layer calculations instead of having to walk a single sample all the way through the net before starting on the next.
This is something I'd like to do, yes-- |
I guess I can't see what kind of project wouldn't need an anti-pop solution. And the solution is model-type-specific. LSTM models don't need anything at all. WaveNet is dependent on the size of the receptive field. It doesn't seem ideal to push that complexity outside of the core NAM code.
I haven't used (or seen) any parametric models - can you explain why doing a pre-warm wouldn't work in that case and the current anti-pop would?
The fact that the pre-warm is done in the background (and potentially on a lower-priority thread) is one of the key benefits. It doesn't take cycles away from the real-time processing throughput (which the current anti-pop method does do ). |
A few examples off the top of my head:
There's a lot of use cases that beyond the implementation in NeuralAmpModelerPlugin, which is only intended as an example.
The value of params you gave, as an empty dict, would be invalid. In terms of e.g. a model like the amp in the parameteric modeling video, the params would look like
and providing an empty dict wouldn't be what the model expects. By contrast, the current anti-pop only needs the output buffer to operate on.
Good point. Though the code I suggested could be run in that same thread if that's a consideration for you? (Like I said, there are multiple valid ways to assess the trade-off, and I don't want the code to force one over the others). |
Sorry - by "anti-pop" solution I meant either the current one, or the pre-warm. It seems like you would always want something and not the un-stabilized initial behavior.
It seems like in that case, it would make sense to pass an initial set of parameters at model creation time, and pre-warm on that. My key points remain:
From my perspective as a NAM core API consumer, I just want it to work - I don't care about model types, receptive fields, etc... |
[Thanks for your patience / Apologies as some of this will probably be repeating myself from the discussion on #61] I see the rationale for your key points, and I personally agree with many of them. The key sticking point is whether this would be a breaking change. I checked back through the code to see if there's a chance that this might "just work". While it happens to be the (convenient!) case that passing an empty params dict to However, I'm kind of keen to put a stake in the ground and get on with making some breaking changes for v0.1. And I'd be happy to accept this PR under those conditions. So here's what I plan on doing, in order:
Sound good? |
👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! 👍🏻
|
||
long receptive_field = 1; | ||
for (size_t i = 0; i < this->_layer_arrays.size(); i++) | ||
receptive_field += this->_layer_arrays[i].get_receptive_field(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is a receptive field getter not defined for WaveNet? That'd be handy/surely this exists somewhere else in the code?
...looks like no! Huh.
On toward v0.1! |
👍 |
This PR switches WaveNet from using the current anti-pop method to pre-warming the model over the size of the receptive field on creation.
This has the advantage that the pre-warming isn't done during real-time processing, and it also does not need the volume ramped-in transition.
I used a single-sample buffer for simplicity, and to be conservative since we do not yet know the buffer size. I figure there isn't much cross-sample optimization happening anyway. This could be changed if we get around to telling the model its maximum audio buffer size.