-
Notifications
You must be signed in to change notification settings - Fork 62
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
Processing interface cleanup #78
Changes from all commits
0338669
7c1c4e9
5ce9463
fc236d4
e8e8af6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,15 +32,15 @@ DSP::DSP(const double loudness, const double expected_sample_rate) | |
{ | ||
} | ||
|
||
void DSP::process(NAM_SAMPLE** inputs, NAM_SAMPLE** outputs, const int num_channels, const int num_frames, | ||
const double input_gain, const double output_gain, | ||
const std::unordered_map<std::string, double>& params) | ||
void DSP::process(NAM_SAMPLE* input, NAM_SAMPLE* output, const int num_frames) | ||
{ | ||
this->_get_params_(params); | ||
this->_apply_input_level_(inputs, num_channels, num_frames, input_gain); | ||
this->_input_samples = input; | ||
this->_output_samples = output; | ||
this->_num_input_samples = num_frames; | ||
|
||
this->_ensure_core_dsp_output_ready_(); | ||
this->_process_core_(); | ||
this->_apply_output_level_(outputs, num_channels, num_frames, output_gain); | ||
this->_apply_output_level_(output, _num_input_samples); | ||
} | ||
|
||
void DSP::finalize_(const int num_frames) {} | ||
|
@@ -60,38 +60,23 @@ void DSP::_get_params_(const std::unordered_map<std::string, double>& input_para | |
} | ||
} | ||
|
||
void DSP::_apply_input_level_(NAM_SAMPLE** inputs, const int num_channels, const int num_frames, const double gain) | ||
{ | ||
// Must match exactly; we're going to use the size of _input_post_gain later | ||
// for num_frames. | ||
if ((int)this->_input_post_gain.size() != num_frames) | ||
this->_input_post_gain.resize(num_frames); | ||
// MONO ONLY | ||
const int channel = 0; | ||
for (int i = 0; i < num_frames; i++) | ||
this->_input_post_gain[i] = float(gain * inputs[channel][i]); | ||
} | ||
|
||
void DSP::_ensure_core_dsp_output_ready_() | ||
{ | ||
if (this->_core_dsp_output.size() < this->_input_post_gain.size()) | ||
this->_core_dsp_output.resize(this->_input_post_gain.size()); | ||
} | ||
|
||
void DSP::_process_core_() | ||
{ | ||
// Default implementation is the null operation | ||
for (size_t i = 0; i < this->_input_post_gain.size(); i++) | ||
this->_core_dsp_output[i] = this->_input_post_gain[i]; | ||
for (size_t i = 0; i < _num_input_samples; i++) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: yeah, I'm probably going to PR to put this as the default implementation of |
||
this->_output_samples[i] = _input_samples[i]; | ||
} | ||
|
||
void DSP::_apply_output_level_(NAM_SAMPLE** outputs, const int num_channels, const int num_frames, const double gain) | ||
void DSP::_apply_output_level_(NAM_SAMPLE* output, const int num_frames) | ||
{ | ||
const double loudnessGain = pow(10.0, -(this->mLoudness - TARGET_DSP_LOUDNESS) / 20.0); | ||
const double finalGain = this->mNormalizeOutputLoudness ? gain * loudnessGain : gain; | ||
for (int c = 0; c < num_channels; c++) | ||
for (int s = 0; s < num_frames; s++) | ||
outputs[c][s] = (NAM_SAMPLE)(finalGain * this->_core_dsp_output[s]); | ||
const double finalGain = this->mNormalizeOutputLoudness ? loudnessGain : 1.0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I see. Question: What do you think about having this class not be responsible for normalizing? It can provide information, but leave it up to the plugin to do anything about it? I'm happy either way, but perhaps a tiny bit happier pushing it out of this library because of how it'd clean up all of the subclasses and make it a little simpler from a dev standpoint I think. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, that was my preference from the beginning: #18 (comment) 😉 I think it makes sense for the plugin to do it, since it will already be doing output gain adjustment - so no additional pass necessary. |
||
for (int s = 0; s < num_frames; s++) | ||
output[s] = (NAM_SAMPLE)(finalGain * _output_samples[s]); | ||
} | ||
|
||
// Buffer ===================================================================== | ||
|
@@ -122,11 +107,11 @@ void Buffer::_set_receptive_field(const int new_receptive_field, const int input | |
|
||
void Buffer::_update_buffers_() | ||
{ | ||
const long int num_frames = this->_input_post_gain.size(); | ||
// Make sure that the buffer is big enough for the receptive field and the | ||
// frames needed! | ||
{ | ||
const long minimum_input_buffer_size = (long)this->_receptive_field + _INPUT_BUFFER_SAFETY_FACTOR * num_frames; | ||
const long minimum_input_buffer_size = | ||
(long)this->_receptive_field + _INPUT_BUFFER_SAFETY_FACTOR * _num_input_samples; | ||
if ((long)this->_input_buffer.size() < minimum_input_buffer_size) | ||
{ | ||
long new_buffer_size = 2; | ||
|
@@ -139,13 +124,13 @@ void Buffer::_update_buffers_() | |
|
||
// If we'd run off the end of the input buffer, then we need to move the data | ||
// back to the start of the buffer and start again. | ||
if (this->_input_buffer_offset + num_frames > (long)this->_input_buffer.size()) | ||
if (this->_input_buffer_offset + _num_input_samples > (long)this->_input_buffer.size()) | ||
this->_rewind_buffers_(); | ||
// Put the new samples into the input buffer | ||
for (long i = this->_input_buffer_offset, j = 0; j < num_frames; i++, j++) | ||
this->_input_buffer[i] = this->_input_post_gain[j]; | ||
for (long i = this->_input_buffer_offset, j = 0; j < _num_input_samples; i++, j++) | ||
this->_input_buffer[i] = _input_samples[j]; | ||
// And resize the output buffer: | ||
this->_output_buffer.resize(num_frames); | ||
this->_output_buffer.resize(_num_input_samples); | ||
std::fill (this->_output_buffer.begin(), this->_output_buffer.end(), 0.0f); | ||
} | ||
|
||
|
@@ -203,11 +188,11 @@ void Linear::_process_core_() | |
this->Buffer::_update_buffers_(); | ||
|
||
// Main computation! | ||
for (size_t i = 0; i < this->_input_post_gain.size(); i++) | ||
for (size_t i = 0; i < _num_input_samples; i++) | ||
{ | ||
const size_t offset = this->_input_buffer_offset - this->_weight.size() + i + 1; | ||
auto input = Eigen::Map<const Eigen::VectorXf>(&this->_input_buffer[offset], this->_receptive_field); | ||
this->_core_dsp_output[i] = this->_bias + this->_weight.dot(input); | ||
this->_output_samples[i] = this->_bias + this->_weight.dot(input); | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,18 +48,13 @@ class DSP | |
DSP(const double expected_sample_rate = -1.0); | ||
DSP(const double loudness, const double expected_sample_rate = -1.0); | ||
virtual ~DSP() = default; | ||
// process() does all of the processing requried to take `inputs` array and | ||
// fill in the required values on `outputs`. | ||
// process() does all of the processing requried to take `input` array and | ||
// fill in the required values on `output`. | ||
// To do this: | ||
// 1. The parameters from the plugin (I/O levels and any other parametric | ||
// inputs) are gotten. | ||
// 2. The input level is applied | ||
// 3. The core DSP algorithm is run (This is what should probably be | ||
// 1. The core DSP algorithm is run (This is what should probably be | ||
// overridden in subclasses). | ||
// 4. The output level is applied and the result stored to `output`. | ||
virtual void process(NAM_SAMPLE** inputs, NAM_SAMPLE** outputs, const int num_channels, const int num_frames, | ||
const double input_gain, const double output_gain, | ||
const std::unordered_map<std::string, double>& params); | ||
// 2. The output level is applied and the result stored to `output`. | ||
virtual void process(NAM_SAMPLE* input, NAM_SAMPLE* output, const int num_frames); | ||
// Anything to take care of before next buffer comes in. | ||
// For example: | ||
// * Move the buffer index forward | ||
|
@@ -82,10 +77,12 @@ class DSP | |
std::unordered_map<std::string, double> _params; | ||
// If the params have changed since the last buffer was processed: | ||
bool _stale_params; | ||
// Where to store the samples after applying input gain | ||
std::vector<float> _input_post_gain; | ||
// Location for the output of the core DSP algorithm. | ||
std::vector<float> _core_dsp_output; | ||
// Input sample buffer | ||
NAM_SAMPLE* _input_samples; | ||
// Output sample buffer | ||
NAM_SAMPLE* _output_samples; | ||
// Number of samples in the input buffer | ||
int _num_input_samples; | ||
|
||
// Methods | ||
|
||
|
@@ -94,10 +91,6 @@ class DSP | |
// (TODO use "listener" approach) | ||
void _get_params_(const std::unordered_map<std::string, double>& input_params); | ||
|
||
// Apply the input gain | ||
// Result populates this->_input_post_gain | ||
void _apply_input_level_(NAM_SAMPLE** inputs, const int num_channels, const int num_frames, const double gain); | ||
|
||
// i.e. ensure the size is correct. | ||
void _ensure_core_dsp_output_ready_(); | ||
|
||
|
@@ -107,7 +100,7 @@ class DSP | |
virtual void _process_core_(); | ||
|
||
// Copy this->_core_dsp_output to output and apply the output volume | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Since we're getting rid of |
||
void _apply_output_level_(NAM_SAMPLE** outputs, const int num_channels, const int num_frames, const double gain); | ||
void _apply_output_level_(NAM_SAMPLE* output, const int num_frames); | ||
}; | ||
|
||
// Class where an input buffer is kept so that long-time effects can be | ||
|
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.
Nit: I bet we can simplify this even more: I think that
process()
can be a pure virtual function.