diff options
Diffstat (limited to 'src/MemlessPoly.cpp')
-rw-r--r-- | src/MemlessPoly.cpp | 133 |
1 files changed, 94 insertions, 39 deletions
diff --git a/src/MemlessPoly.cpp b/src/MemlessPoly.cpp index 7e074eb..b0d950c 100644 --- a/src/MemlessPoly.cpp +++ b/src/MemlessPoly.cpp @@ -36,6 +36,7 @@ #include <stdio.h> #include <stdexcept> +#include <future> #include <array> #include <iostream> #include <fstream> @@ -43,22 +44,36 @@ using namespace std; +#define NUM_COEFS 5 // By default the signal is unchanged -static const std::array<float, 8> default_coefficients({ +static const std::array<complexf, 8> default_coefficients({{ 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 - }); + }}); -MemlessPoly::MemlessPoly(const std::string& coefs_file) : +MemlessPoly::MemlessPoly(const std::string& coefs_file, unsigned int num_threads) : PipelinedModCodec(), RemoteControllable("memlesspoly"), - m_coefs_file(coefs_file) + m_num_threads(num_threads), + m_coefs(), + m_coefs_file(coefs_file), + m_coefs_mutex() { PDEBUG("MemlessPoly::MemlessPoly(%s) @ %p\n", coefs_file.c_str(), this); + if (m_num_threads == 0) { + const unsigned int hw_concurrency = std::thread::hardware_concurrency(); + etiLog.level(info) << "Polynomial Predistorter will use " << + hw_concurrency << " threads (auto detected)"; + } + else { + etiLog.level(info) << "Polynomial Predistorter will use " << + m_num_threads << " threads (set in config file)"; + } + RC_ADD_PARAMETER(ncoefs, "(Read-only) number of coefficients."); RC_ADD_PARAMETER(coeffile, "Filename containing coefficients. When written to, the new file gets automatically loaded."); @@ -69,41 +84,42 @@ MemlessPoly::MemlessPoly(const std::string& coefs_file) : void MemlessPoly::load_coefficients(const std::string &coefFile) { - std::vector<float> coefs; + std::vector<complexf> coefs; if (coefFile == "default") { std::copy(default_coefficients.begin(), default_coefficients.end(), std::back_inserter(coefs)); } else { std::ifstream coef_fstream(coefFile.c_str()); - if(!coef_fstream) { - fprintf(stderr, "MemlessPoly: file %s could not be opened !\n", coefFile.c_str()); - throw std::runtime_error("MemlessPoly: Could not open file with coefs! "); + if (!coef_fstream) { + throw std::runtime_error("MemlessPoly: Could not open file with coefs!"); } int n_coefs; coef_fstream >> n_coefs; if (n_coefs <= 0) { - fprintf(stderr, "MemlessPoly: warning: coefs file has invalid format\n"); throw std::runtime_error("MemlessPoly: coefs file has invalid format."); } - - if (n_coefs != 8) { - throw std::runtime_error( "MemlessPoly: error: coefs file does not have 8 coefs\n"); + else if (n_coefs != NUM_COEFS) { + throw std::runtime_error("MemlessPoly: invalid number of coefs: " + + std::to_string(coefs.size())); } - fprintf(stderr, "MemlessPoly: Reading %d coefs...\n", n_coefs); + etiLog.log(debug, "MemlessPoly: Reading %d coefs...", n_coefs); coefs.resize(n_coefs); - int n; - for (n = 0; n < n_coefs; n++) { - coef_fstream >> coefs[n]; - PDEBUG("MemlessPoly: coef: %f\n", coefs[n] ); + for (int n = 0; n < n_coefs; n++) { + float a, b; + coef_fstream >> a; + coef_fstream >> b; + coefs[n] = complexf(a, b); + if (coef_fstream.eof()) { - fprintf(stderr, "MemlessPoly: file %s should contains %d coefs, but EOF reached "\ - "after %d coefs !\n", coefFile.c_str(), n_coefs, n); - throw std::runtime_error("MemlessPoly: coefs file invalid ! "); + etiLog.log(error, "MemlessPoly: file %s should contains %d coefs, " + "but EOF reached after %d coefs !", + coefFile.c_str(), n_coefs, n); + throw std::runtime_error("MemlessPoly: coefs file invalid !"); } } } @@ -115,30 +131,69 @@ void MemlessPoly::load_coefficients(const std::string &coefFile) } } +static void apply_coeff( + const vector<complexf> &coefs, + const complexf* in, size_t start, size_t stop, + complexf* out) +{ + for (size_t i = start; i < stop; i++) { + + /* Implement + a0 + a1*x + a2*x^2 + a3*x^3 + a4*x^4 + a5*x^5; + with less multiplications: + a0 + x*(a1 + x*(a2 + x*(a3 + x*(a3 + x*(a4 + a5*x))))); + */ + + /* Make sure to adapt NUM_COEFS when you change this */ + out[i] = + coefs[0] + in[i] * + ( coefs[1] + in[i] * + ( coefs[2] + in[i] * + ( coefs[3] + in[i] * + ( coefs[4] + in[i] * + ( coefs[5] + in[i] ))))); + } +} int MemlessPoly::internal_process(Buffer* const dataIn, Buffer* dataOut) { - const float* in = reinterpret_cast<const float*>(dataIn->getData()); - float* out = reinterpret_cast<float*>(dataOut->getData()); - size_t sizeIn = dataIn->getLength() / sizeof(float); - - { - std::lock_guard<std::mutex> lock(m_coefs_mutex); - for (size_t i = 0; i < sizeIn; i += 1) { - float mag = std::abs(in[i]); - //out[i] = in[i]; - out[i] = in[i] * ( - m_coefs[0] + - m_coefs[1] * mag + - m_coefs[2] * mag*mag + - m_coefs[3] * mag*mag*mag + - m_coefs[4] * mag*mag*mag*mag + - m_coefs[5] * mag*mag*mag*mag*mag + - m_coefs[6] * mag*mag*mag*mag*mag*mag + - m_coefs[7] * mag*mag*mag*mag*mag*mag*mag - ); + dataOut->setLength(dataIn->getLength()); + + const complexf* in = reinterpret_cast<const complexf*>(dataIn->getData()); + complexf* out = reinterpret_cast<complexf*>(dataOut->getData()); + size_t sizeOut = dataOut->getLength() / sizeof(complexf); + + { + std::lock_guard<std::mutex> lock(m_coefs_mutex); + const unsigned int hw_concurrency = std::thread::hardware_concurrency(); + + const unsigned int num_threads = + (m_num_threads > 0) ? m_num_threads : hw_concurrency; + + if (num_threads) { + const size_t step = sizeOut / num_threads; + vector<future<void> > flags; + + size_t start = 0; + for (size_t i = 0; i < num_threads - 1; i++) { + flags.push_back(async(launch::async, apply_coeff, + m_coefs, in, start, start + step, out)); + + start += step; + } + + // Do the last in this thread + apply_coeff(m_coefs, in, start, sizeOut, out); + + // Wait for completion of the tasks + for (auto& f : flags) { + f.get(); } } + else { + apply_coeff(m_coefs, in, 0, sizeOut, out); + } + } return dataOut->getLength(); } |