diff options
| author | Carl Hetherington <cth@carlh.net> | 2020-04-19 22:01:51 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2020-04-20 00:31:10 +0200 |
| commit | 20cd6dd0a9fa278338c98d385fb75e3e28835ece (patch) | |
| tree | bd2962a371f457a23ad4545335c31e1d43578be1 /src | |
| parent | 56904c466e3e0b62a6416c1870333132bc9e9f5f (diff) | |
Offer a class for calculating in blocks, rather than a method.
Diffstat (limited to 'src')
| -rw-r--r-- | src/leqm-nrt.cc | 181 | ||||
| -rw-r--r-- | src/leqm-nrt.h | 129 |
2 files changed, 202 insertions, 108 deletions
diff --git a/src/leqm-nrt.cc b/src/leqm-nrt.cc index 6739ff1..762f7a6 100644 --- a/src/leqm-nrt.cc +++ b/src/leqm-nrt.cc @@ -44,62 +44,6 @@ // Version 0.0.18 (C) Luca Trisciani 2011-2013, 2017-2018 // Tool from the DCP-Werkstatt Software Bundle -class Sum -{ -public: - void sum_samples(std::vector<double> const& input_samples, std::vector<double> const& c_input_samples, int nsamples) - { - _mutex.lock(); - _nsamples += nsamples; - for (auto i = 0; i < nsamples; i++) { - _sum += input_samples[i]; - _csum += c_input_samples[i]; - } - _mutex.unlock(); - } - - int nsamples() const - { - return _nsamples; - } - - /* - How the final offset is calculated without reference to a test tone: - P0 is the SPL reference 20 uPa - Reference SPL is RMS ! So 85 SPL over 20 uPa is 10^4.25 x 0.000020 = 0.355655882 Pa (RMS), - but Peak value is 0.355655882 x sqr(2) = 0.502973372 that is 20 x log ( 0.502973372 / 0.000020) = 88.010299957 - To that one has to add the 20 dB offset of the reference -20dBFS: 88.010299957 + 20.00 = 108.010299957 - - But ISO 21727:2004(E) ask for a reference level "measured using an average responding meter". So reference level is not 0.707, but 0.637 = 2/pi - */ - - double rms() const - { - return 20 * log10(mean()) + 108.010299957; - } - - double leqm() const - { - return 20 * log10(cmean()) + 108.010299957; - } - -private: - double mean() const - { - return pow(_sum / _nsamples, 0.500); - } - - double cmean() const - { - return pow(_csum / _nsamples, 0.500); - } - - double _csum = 0.0; // convolved sum - double _sum = 0.0; // flat sum - int _nsamples = 0; - std::mutex _mutex; -}; - class Worker { @@ -328,10 +272,7 @@ Result calculate_file( return -1; } - auto result = calculate_function( - [file](double* buffer, int64_t samples) -> int64_t { - return sf_read_double(file, buffer, samples); - }, + Calculator calculator( sf_info.channels, sf_info.samplerate, bitdepth, @@ -341,9 +282,20 @@ Result calculate_file( num_cpu ); + while (true) { + std::vector<double> buffer(4096); + auto read = sf_read_double(file, buffer.data(), buffer.size()); + if (read <= 0) { + break; + } + + buffer.resize(read); + calculator.add(buffer); + } + sf_close(file); - return result; + return {calculator.leq_m(), calculator.leq_nw()}; } @@ -371,68 +323,101 @@ std::vector<double> default_channel_corrections(int channels) } -Result calculate_function( - std::function<int64_t (double*, int64_t)> get_audio_data, - int channels, - int sample_rate, - int bits_per_sample, - std::vector<double> channel_corrections, - int buffer_size_ms, - int number_of_filter_interpolation_points, - int num_cpu +double convert_log_to_linear_single(double in) +{ + return powf(10, in / 20.0f); +} + + +Calculator::Calculator( + int channels, + int sample_rate, + int bits_per_sample, + std::vector<double> channel_corrections, + int buffer_size_ms, + int number_of_filter_interpolation_points, + int num_cpu ) + : _channels(channels) + , _channel_corrections(channel_corrections) + , _number_of_filter_interpolation_points(number_of_filter_interpolation_points) + , _num_cpu(num_cpu) { if ((sample_rate * buffer_size_ms) % 1000) { - return -102; + throw BadBufferSizeError(); } if (static_cast<int>(channel_corrections.size()) != channels) { channel_corrections = default_channel_corrections(channels); } if (static_cast<int>(channel_corrections.size()) != channels) { - return {100}; + throw BadChannelCorrectionsError(); } - auto ir = calculate_ir(number_of_filter_interpolation_points, sample_rate, bits_per_sample); - - Sum totsum; + _ir = calculate_ir(number_of_filter_interpolation_points, sample_rate, bits_per_sample); + _buffer.resize((sample_rate * channels * buffer_size_ms) / 1000); +} - std::vector<std::shared_ptr<Worker>> workers; - int const buffer_size_samples = (sample_rate * channels * buffer_size_ms) / 1000; - std::vector<double> buffer(buffer_size_samples); - while (true) { - auto samples_read = get_audio_data(buffer.data(), buffer_size_samples); - if (samples_read <= 0) { - break; - } +void Calculator::process_buffer() +{ + if (_buffer_free_offset == 0) { + return; + } - workers.push_back( + _workers.push_back( std::make_shared<Worker>( - buffer, - samples_read, - channels, - number_of_filter_interpolation_points, - ir, - &totsum, - channel_corrections + _buffer, + _buffer_free_offset, + _channels, + _number_of_filter_interpolation_points, + _ir, + &_sum, + _channel_corrections ) ); - if (static_cast<int>(workers.size()) == num_cpu) { - workers.clear(); - } + if (static_cast<int>(_workers.size()) == _num_cpu) { + _workers.clear(); } - workers.clear(); + _buffer_free_offset = 0; +} + - return {totsum.leqm(), totsum.rms()}; +void Calculator::add(std::vector<double> samples) +{ + size_t samples_offset = 0; + + while (samples_offset < samples.size()) { + /* Copy some data into our buffer */ + auto to_copy = std::min(samples.size() - samples_offset, _buffer.size() - _buffer_free_offset); + memcpy(_buffer.data() + _buffer_free_offset, samples.data() + samples_offset, to_copy * sizeof(double)); + samples_offset += to_copy; + _buffer_free_offset += to_copy; + + /* Process the buffer if it's full */ + if (_buffer_free_offset == _buffer.size()) { + process_buffer(); + } + } } -double convert_log_to_linear_single(double in) +double Calculator::leq_m() { - return powf(10, in / 20.0f); + process_buffer(); + _workers.clear(); + + return _sum.leqm(); } +double Calculator::leq_nw() +{ + process_buffer(); + _workers.clear(); + + return _sum.rms(); +} + diff --git a/src/leqm-nrt.h b/src/leqm-nrt.h index fd1aba1..a72c299 100644 --- a/src/leqm-nrt.h +++ b/src/leqm-nrt.h @@ -1,6 +1,67 @@ #include <string> #include <vector> #include <functional> +#include <stdexcept> +#include <mutex> +#include <cmath> +#include <memory> + +class Sum +{ +public: + void sum_samples(std::vector<double> const& input_samples, std::vector<double> const& c_input_samples, int nsamples) + { + _mutex.lock(); + _nsamples += nsamples; + for (auto i = 0; i < nsamples; i++) { + _sum += input_samples[i]; + _csum += c_input_samples[i]; + } + _mutex.unlock(); + } + + int nsamples() const + { + return _nsamples; + } + + /* + How the final offset is calculated without reference to a test tone: + P0 is the SPL reference 20 uPa + Reference SPL is RMS ! So 85 SPL over 20 uPa is 10^4.25 x 0.000020 = 0.355655882 Pa (RMS), + but Peak value is 0.355655882 x sqr(2) = 0.502973372 that is 20 x log ( 0.502973372 / 0.000020) = 88.010299957 + To that one has to add the 20 dB offset of the reference -20dBFS: 88.010299957 + 20.00 = 108.010299957 + + But ISO 21727:2004(E) ask for a reference level "measured using an average responding meter". So reference level is not 0.707, but 0.637 = 2/pi + */ + + double rms() const + { + return 20 * log10(mean()) + 108.010299957; + } + + double leqm() const + { + return 20 * log10(cmean()) + 108.010299957; + } + +private: + double mean() const + { + return pow(_sum / _nsamples, 0.500); + } + + double cmean() const + { + return pow(_csum / _nsamples, 0.500); + } + + double _csum = 0.0; // convolved sum + double _sum = 0.0; // flat sum + int _nsamples = 0; + std::mutex _mutex; +}; + struct Result { @@ -41,16 +102,64 @@ Result calculate_file( ); -Result calculate_function( - std::function<int64_t (double*, int64_t)> get_audio_data, - int channels, - int sample_rate, - int bits_per_sample, - std::vector<double> channel_corrections, - int buffer_size_ms, - int number_of_filter_interpolation_points, - int num_cpu +double convert_log_to_linear_single(double in); + + +class BadBufferSizeError : public std::runtime_error +{ +public: + BadBufferSizeError() + : std::runtime_error("Buffer size does not correspond to an integer number of samples") + {} +}; + + +class BadChannelCorrectionsError : public std::runtime_error +{ +public: + BadChannelCorrectionsError() + : std::runtime_error("Incorrect number of channel corrections given, and no defaults are available") + {} +}; + + +class Worker; + + +class Calculator +{ +public: + Calculator( + int channels, + int sample_rate, + int bits_per_sample, + std::vector<double> channel_corrections, + int buffer_size_ms, + int number_of_filter_interpolation_points, + int num_cpu ); + Calculator(Calculator&) = delete; + Calculator(Calculator&&) = delete; + bool operator=(Calculator&) = delete; + bool operator=(Calculator&&) = delete; + + void add(std::vector<double> samples); + + double leq_m(); + double leq_nw(); + +private: + void process_buffer(); + + int _channels; + std::vector<double> _channel_corrections; + int _number_of_filter_interpolation_points; + int _num_cpu; + std::vector<std::shared_ptr<Worker>> _workers; + Sum _sum; + std::vector<double> _ir; + std::vector<double> _buffer; + size_t _buffer_free_offset = 0; +}; -double convert_log_to_linear_single(double in); |
