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
87 changes: 0 additions & 87 deletions NAM/dsp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
#include <unordered_set>

#include "dsp.h"
#include "registry.h"

#define tanh_impl_ std::tanh
// #define tanh_impl_ fast_tanh_

Expand Down Expand Up @@ -250,91 +248,6 @@ void nam::Buffer::_advance_input_buffer_(const int num_frames)
this->_input_buffer_offset += num_frames;
}

// Linear =====================================================================

nam::Linear::Linear(const int in_channels, const int out_channels, const int receptive_field, const bool _bias,
const std::vector<float>& weights, const double expected_sample_rate)
: nam::Buffer(in_channels, out_channels, receptive_field, expected_sample_rate)
{
if ((int)weights.size() != (receptive_field + (_bias ? 1 : 0)))
throw std::runtime_error(
"Params vector does not match expected size based "
"on architecture parameters");

this->_weight.resize(this->_receptive_field);
// Pass in in reverse order so that dot products work out of the box.
for (int i = 0; i < this->_receptive_field; i++)
this->_weight(i) = weights[receptive_field - 1 - i];
this->_bias = _bias ? weights[receptive_field] : (float)0.0;
}

void nam::Linear::process(NAM_SAMPLE** input, NAM_SAMPLE** output, const int num_frames)
{
this->nam::Buffer::_update_buffers_(input, num_frames);

const int in_channels = NumInputChannels();
const int out_channels = NumOutputChannels();

// For now, Linear processes each input channel independently to corresponding output channel
// This is a simple implementation - can be extended later for cross-channel mixing
const int channelsToProcess = std::min(in_channels, out_channels);

// Main computation!
for (int ch = 0; ch < channelsToProcess; ch++)
{
for (int i = 0; i < num_frames; i++)
{
const long offset = this->_input_buffer_offset - this->_weight.size() + i + 1;
auto input_vec = Eigen::Map<const Eigen::VectorXf>(&this->_input_buffers[ch][offset], this->_receptive_field);
output[ch][i] = this->_bias + this->_weight.dot(input_vec);
}
}

// Zero out any extra output channels
for (int ch = channelsToProcess; ch < out_channels; ch++)
{
for (int i = 0; i < num_frames; i++)
output[ch][i] = (NAM_SAMPLE)0.0;
}

// Prepare for next call:
nam::Buffer::_advance_input_buffer_(num_frames);
}

// Config parser
nam::linear::LinearConfig nam::linear::parse_config_json(const nlohmann::json& config)
{
LinearConfig c;
c.receptive_field = config["receptive_field"];
c.bias = config["bias"];
// Default to 1 channel in/out for backward compatibility
c.in_channels = config.value("in_channels", 1);
c.out_channels = config.value("out_channels", 1);
return c;
}

// LinearConfig::create()
std::unique_ptr<nam::DSP> nam::linear::LinearConfig::create(std::vector<float> weights, double sampleRate)
{
return std::make_unique<nam::Linear>(in_channels, out_channels, receptive_field, bias, weights, sampleRate);
}

// Config parser for ConfigParserRegistry
std::unique_ptr<nam::ModelConfig> nam::linear::create_config(const nlohmann::json& config, double sampleRate)
{
(void)sampleRate;
auto c = std::make_unique<LinearConfig>();
auto parsed = parse_config_json(config);
*c = parsed;
return c;
}

// Register the config parser
namespace
{
static nam::ConfigParserHelper _register_Linear("Linear", nam::linear::create_config);
}

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

// Conv1x1 ====================================================================
Expand Down
55 changes: 2 additions & 53 deletions NAM/dsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,59 +240,6 @@ class Buffer : public DSP
virtual void _rewind_buffers_();
};

/// \brief Basic linear model
///
/// Implements a simple linear convolution, (i.e. an impulse response).
class Linear : public Buffer
{
public:
/// \brief Constructor
/// \param in_channels Number of input channels
/// \param out_channels Number of output channels
/// \param receptive_field Size of the impulse response
/// \param _bias Whether to use bias
/// \param weights Model weights (impulse response coefficients)
/// \param expected_sample_rate Expected sample rate in Hz (-1.0 if unknown)
Linear(const int in_channels, const int out_channels, const int receptive_field, const bool _bias,
const std::vector<float>& weights, const double expected_sample_rate = -1.0);

/// \brief Process audio frames
/// \param input Input audio buffers
/// \param output Output audio buffers
/// \param num_frames Number of frames to process
void process(NAM_SAMPLE** input, NAM_SAMPLE** output, const int num_frames) override;

protected:
Eigen::VectorXf _weight;
float _bias;
};

namespace linear
{

/// \brief Configuration for a Linear model
struct LinearConfig : public ModelConfig
{
int receptive_field;
bool bias;
int in_channels;
int out_channels;

std::unique_ptr<DSP> create(std::vector<float> weights, double sampleRate) override;
};

/// \brief Parse Linear configuration from JSON
/// \param config JSON configuration object
/// \return LinearConfig
LinearConfig parse_config_json(const nlohmann::json& config);

/// \brief Config parser for ConfigParserRegistry
/// \param config JSON configuration object
/// \param sampleRate Expected sample rate in Hz
/// \return unique_ptr<ModelConfig> wrapping a LinearConfig
std::unique_ptr<ModelConfig> create_config(const nlohmann::json& config, double sampleRate);
} // namespace linear

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

/// \brief 1x1 convolution (really just a fully-connected linear layer operating per-sample)
Expand Down Expand Up @@ -396,3 +343,5 @@ void verify_config_version(const std::string version);
/// \return Unique pointer to a DSP object
std::unique_ptr<DSP> get_dsp_legacy(const std::filesystem::path dirname);
}; // namespace nam

#include "linear.h"
85 changes: 85 additions & 0 deletions NAM/linear.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include "linear.h"

#include <algorithm>
#include <stdexcept>

#include "registry.h"

nam::Linear::Linear(const int in_channels, const int out_channels, const int receptive_field, const bool _bias,
const std::vector<float>& weights, const double expected_sample_rate)
: nam::Buffer(in_channels, out_channels, receptive_field, expected_sample_rate)
{
if ((int)weights.size() != (receptive_field + (_bias ? 1 : 0)))
throw std::runtime_error(
"Params vector does not match expected size based "
"on architecture parameters");

this->_weight.resize(this->_receptive_field);
// Pass in in reverse order so that dot products work out of the box.
for (int i = 0; i < this->_receptive_field; i++)
this->_weight(i) = weights[receptive_field - 1 - i];
this->_bias = _bias ? weights[receptive_field] : (float)0.0;
}

void nam::Linear::process(NAM_SAMPLE** input, NAM_SAMPLE** output, const int num_frames)
{
this->nam::Buffer::_update_buffers_(input, num_frames);

const int in_channels = NumInputChannels();
const int out_channels = NumOutputChannels();

// For now, Linear processes each input channel independently to corresponding output channel
// This is a simple implementation - can be extended later for cross-channel mixing
const int channelsToProcess = std::min(in_channels, out_channels);

// Main computation!
for (int ch = 0; ch < channelsToProcess; ch++)
{
for (int i = 0; i < num_frames; i++)
{
const long offset = this->_input_buffer_offset - this->_weight.size() + i + 1;
auto input_vec = Eigen::Map<const Eigen::VectorXf>(&this->_input_buffers[ch][offset], this->_receptive_field);
output[ch][i] = this->_bias + this->_weight.dot(input_vec);
}
}

// Zero out any extra output channels
for (int ch = channelsToProcess; ch < out_channels; ch++)
{
for (int i = 0; i < num_frames; i++)
output[ch][i] = (NAM_SAMPLE)0.0;
}

// Prepare for next call:
nam::Buffer::_advance_input_buffer_(num_frames);
}

nam::linear::LinearConfig nam::linear::parse_config_json(const nlohmann::json& config)
{
LinearConfig c;
c.receptive_field = config["receptive_field"];
c.bias = config["bias"];
// Default to 1 channel in/out for backward compatibility
c.in_channels = config.value("in_channels", 1);
c.out_channels = config.value("out_channels", 1);
return c;
}

std::unique_ptr<nam::DSP> nam::linear::LinearConfig::create(std::vector<float> weights, double sampleRate)
{
return std::make_unique<nam::Linear>(in_channels, out_channels, receptive_field, bias, weights, sampleRate);
}

std::unique_ptr<nam::ModelConfig> nam::linear::create_config(const nlohmann::json& config, double sampleRate)
{
(void)sampleRate;
auto c = std::make_unique<LinearConfig>();
auto parsed = parse_config_json(config);
*c = parsed;
return c;
}

namespace
{
static nam::ConfigParserHelper _register_Linear("Linear", nam::linear::create_config);
}
61 changes: 61 additions & 0 deletions NAM/linear.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#pragma once

#include "dsp.h"

namespace nam
{

/// \brief Basic linear model
///
/// Implements a simple linear convolution, (i.e. an impulse response).
class Linear : public Buffer
{
public:
/// \brief Constructor
/// \param in_channels Number of input channels
/// \param out_channels Number of output channels
/// \param receptive_field Size of the impulse response
/// \param _bias Whether to use bias
/// \param weights Model weights (impulse response coefficients)
/// \param expected_sample_rate Expected sample rate in Hz (-1.0 if unknown)
Linear(const int in_channels, const int out_channels, const int receptive_field, const bool _bias,
const std::vector<float>& weights, const double expected_sample_rate = -1.0);

/// \brief Process audio frames
/// \param input Input audio buffers
/// \param output Output audio buffers
/// \param num_frames Number of frames to process
void process(NAM_SAMPLE** input, NAM_SAMPLE** output, const int num_frames) override;

protected:
Eigen::VectorXf _weight;
float _bias;
};

namespace linear
{

/// \brief Configuration for a Linear model
struct LinearConfig : public ModelConfig
{
int receptive_field;
bool bias;
int in_channels;
int out_channels;

std::unique_ptr<DSP> create(std::vector<float> weights, double sampleRate) override;
};

/// \brief Parse Linear configuration from JSON
/// \param config JSON configuration object
/// \return LinearConfig
LinearConfig parse_config_json(const nlohmann::json& config);

/// \brief Config parser for ConfigParserRegistry
/// \param config JSON configuration object
/// \param sampleRate Expected sample rate in Hz
/// \return unique_ptr<ModelConfig> wrapping a LinearConfig
std::unique_ptr<ModelConfig> create_config(const nlohmann::json& config, double sampleRate);
} // namespace linear

} // namespace nam
Loading