Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions NAM/convnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ nam::convnet::ConvNet::ConvNet(const int channels, const std::vector<int>& dilat
if (it != weights.end())
throw std::runtime_error("Didn't touch all the weights when initializing ConvNet");

_prewarm_samples = 1;
mPrewarmSamples = 1;
for (size_t i = 0; i < dilations.size(); i++)
_prewarm_samples += dilations[i];
mPrewarmSamples += dilations[i];
}


Expand Down
5 changes: 4 additions & 1 deletion NAM/convnet.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class ConvNet : public Buffer
std::vector<float>& weights, const double expected_sample_rate = -1.0);
~ConvNet() = default;

void process(NAM_SAMPLE* input, NAM_SAMPLE* output, const int num_frames) override;

protected:
std::vector<ConvNetBlock> _blocks;
std::vector<Eigen::MatrixXf> _block_vals;
Expand All @@ -81,7 +83,8 @@ class ConvNet : public Buffer
void _update_buffers_(NAM_SAMPLE* input, const int num_frames) override;
void _rewind_buffers_() override;

void process(NAM_SAMPLE* input, NAM_SAMPLE* output, const int num_frames) override;
int mPrewarmSamples = 0; // Pre-compute during initialization
int PrewarmSamples() override { return mPrewarmSamples; };
};
}; // namespace convnet
}; // namespace nam
16 changes: 14 additions & 2 deletions NAM/dsp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ nam::DSP::DSP(const double expected_sample_rate)

void nam::DSP::prewarm()
{
if (_prewarm_samples == 0)
const int prewarmSamples = PrewarmSamples();
if (prewarmSamples == 0)
return;

NAM_SAMPLE sample = 0;
NAM_SAMPLE* sample_ptr = &sample;

// pre-warm the model for a model-specific number of samples
for (long i = 0; i < _prewarm_samples; i++)
for (long i = 0; i < prewarmSamples; i++)
{
this->process(sample_ptr, sample_ptr, 1);
sample = 0;
Expand All @@ -51,6 +52,17 @@ double nam::DSP::GetLoudness() const
return mLoudness;
}

void nam::DSP::Reset(const double sampleRate, const int maxBufferSize)
{
// Some subclasses might want to throw an exception if the sample rate is "wrong".
// This could be under a debugging flag potentially.
mExternalSampleRate = sampleRate;
mHaveExternalSampleRate = true;
mMaxBufferSize = maxBufferSize;

// Subclasses might also want to pre-warm, but let them call that themselves in case
// they want to e.g. do some allocations first.
}
void nam::DSP::SetLoudness(const double loudness)
{
mLoudness = loudness;
Expand Down
24 changes: 22 additions & 2 deletions NAM/dsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class DSP
virtual ~DSP() = default;
// prewarm() does any required intial work required to "settle" model initial conditions
// it can be somewhat expensive, so should not be called during realtime audio processing
// Important: don't expect the model to be outputting zeroes after this. Neural networks
// Don't know that there's anything special about "zero", and forcing this gets rid of
// some possibilities that I dont' want to rule out (e.g. models that "are noisy").
virtual void prewarm();
// process() does all of the processing requried to take `input` array and
// fill in the required values on `output`.
Expand All @@ -60,6 +63,16 @@ class DSP
double GetLoudness() const;
// Get whether the model knows how loud it is.
bool HasLoudness() const { return mHasLoudness; };
// General function for resetting the DSP unit.
// This doesn't call prewarm(). If you want to do that, then you might want to use ResetAndPrewarm().
// See https://github.com/sdatkinson/NeuralAmpModelerCore/issues/96 for the reasoning.
virtual void Reset(const double sampleRate, const int maxBufferSize);
// Reset(), then prewarm()
void ResetAndPrewarm(const double sampleRate, const int maxBufferSize)
{
Reset(sampleRate, maxBufferSize);
prewarm();
}
// Set the loudness, in dB.
// This is usually defined to be the loudness to a standardized input. The trainer has its own, but you can always
// use this to define it a different way if you like yours better.
Expand All @@ -71,8 +84,14 @@ class DSP
double mLoudness = 0.0;
// What sample rate does the model expect?
double mExpectedSampleRate;
// How many samples should be processed during "pre-warming"
int _prewarm_samples = 0;
// Have we been told what the external sample rate is? If so, what is it?
bool mHaveExternalSampleRate = false;
double mExternalSampleRate = -1.0;
// The largest buffer I expect to be told to process:
int mMaxBufferSize;

// How many samples should be processed for me to be considered "warmed up"?
virtual int PrewarmSamples() { return 0; };
};

// Class where an input buffer is kept so that long-time effects can be
Expand Down Expand Up @@ -116,6 +135,7 @@ class Linear : public Buffer

// NN modules =================================================================

// TODO conv could take care of its own ring buffer.
class Conv1D
{
public:
Expand Down
8 changes: 8 additions & 0 deletions NAM/lstm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ void nam::lstm::LSTM::process(NAM_SAMPLE* input, NAM_SAMPLE* output, const int n
output[i] = this->_process_sample(input[i]);
}

int nam::lstm::LSTM::PrewarmSamples()
{
int result = (int)(0.5 * mExpectedSampleRate);
// If the expected sample rate wasn't provided, it'll be -1.
// Make sure something still happens.
return result <= 0 ? 1 : result;
}

float nam::lstm::LSTM::_process_sample(const float x)
{
if (this->_layers.size() == 0)
Expand Down
3 changes: 3 additions & 0 deletions NAM/lstm.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class LSTM : public DSP
~LSTM() = default;

protected:
// Hacky, but a half-second seems to work for most models.
int PrewarmSamples() override;

Eigen::VectorXf _head_weight;
float _head_bias;
void process(NAM_SAMPLE* input, NAM_SAMPLE* output, const int num_frames) override;
Expand Down
4 changes: 2 additions & 2 deletions NAM/wavenet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,9 @@ nam::wavenet::WaveNet::WaveNet(const std::vector<nam::wavenet::LayerArrayParams>
this->_head_output.resize(1, 0); // Mono output!
this->set_weights_(weights);

_prewarm_samples = 1;
mPrewarmSamples = 1;
for (size_t i = 0; i < this->_layer_arrays.size(); i++)
_prewarm_samples += this->_layer_arrays[i].get_receptive_field();
mPrewarmSamples += this->_layer_arrays[i].get_receptive_field();
}

void nam::wavenet::WaveNet::set_weights_(std::vector<float>& weights)
Expand Down
3 changes: 3 additions & 0 deletions NAM/wavenet.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ class WaveNet : public DSP
virtual void _set_condition_array(NAM_SAMPLE* input, const int num_frames);
// Ensure that all buffer arrays are the right size for this num_frames
void _set_num_frames_(const long num_frames);

int mPrewarmSamples = 0; // Pre-compute during initialization
int PrewarmSamples() override { return mPrewarmSamples; };
};
}; // namespace wavenet
}; // namespace nam
17 changes: 13 additions & 4 deletions tools/benchmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ using std::chrono::milliseconds;

#define AUDIO_BUFFER_SIZE 64

double buffer[AUDIO_BUFFER_SIZE];
double inputBuffer[AUDIO_BUFFER_SIZE];
double outputBuffer[AUDIO_BUFFER_SIZE];

int main(int argc, char* argv[])
{
Expand All @@ -38,14 +39,22 @@ int main(int argc, char* argv[])

auto t1 = high_resolution_clock::now();

size_t bufferSize = 64;
size_t numBuffers = (48000 / 64) * 2;
size_t bufferSize = AUDIO_BUFFER_SIZE;
model->Reset(model->GetExpectedSampleRate(), bufferSize);
size_t numBuffers = (48000 / bufferSize) * 2;

// Fill input buffer with zeroes.
// Output buffer doesn't matter.
for (int i = 0; i < AUDIO_BUFFER_SIZE; i++)
{
inputBuffer[i] = 0.0;
}

std::cout << "Running benchmark\n";

for (size_t i = 0; i < numBuffers; i++)
{
model->process(buffer, buffer, AUDIO_BUFFER_SIZE);
model->process(inputBuffer, outputBuffer, AUDIO_BUFFER_SIZE);
}

std::cout << "Finished\n";
Expand Down