diff --git a/native/serverconnection/src/audio/filter/FilterThreshold.cpp b/native/serverconnection/src/audio/filter/FilterThreshold.cpp index 4098812..a7cb891 100644 --- a/native/serverconnection/src/audio/filter/FilterThreshold.cpp +++ b/native/serverconnection/src/audio/filter/FilterThreshold.cpp @@ -2,7 +2,7 @@ #include #include #include "FilterThreshold.h" -#include "../../logger.h" +#include "../processing/AudioVolume.h" using namespace std; using namespace tc::audio; @@ -17,41 +17,9 @@ bool ThresholdFilter::initialize(std::string &, float val, size_t margin) { return true; } -/* technique based on http://www.vttoth.com/CMS/index.php/technical-notes/68 */ -inline float merge_ab(float a, float b, float n) { - /* - * Form: A,B := [0;n] - * IF A <= n/2 AND B <= N/2 - * Z = (2 * A * B) / n - * ELSE - * Z = 2(A + B) - (2/n) * A * B - n - * - * For a range from 0 to 2: Z = 2(A + B) - AB - 2 - */ - - auto half_n = n / 2; - auto inv_half_n = 1 / half_n; - - if(a < half_n && b < half_n) { - return inv_half_n * a * b; - } - - return 2 * (a + b) - inv_half_n * a * b - n; -} - bool ThresholdFilter::process(const void *_buffer) { - float value = -1; - auto analyze_callback = this->on_analyze; - - for(size_t channel = 0; channel < this->_channels; channel++) { - auto percentage = this->analyze(_buffer, channel); - - if(channel == 0) { - value = (float) percentage; - } else { - value = merge_ab(value, (float) percentage, 100); - } - } + auto analyze_callback = this->on_analyze; + float value = audio::audio_buffer_level((const float*) _buffer, this->_channels, this->_frame_size); auto last_level = this->_current_level; float smooth; @@ -75,27 +43,4 @@ bool ThresholdFilter::process(const void *_buffer) { } return (this->_margin_processed_samples += this->_frame_size) < this->_margin_samples; -} - - -long double ThresholdFilter::analyze(const void *_buffer, size_t channel) { - /* equation taken from the web client */ - auto buffer = (float*) _buffer; - - long double value = 0; - auto sample = this->_frame_size; - - while(sample-- > 0) { - /* to be like the web client */ - const auto num = floorl(*buffer * 127) / 127.f; - value += num * num; - buffer += this->_channels; - } - - long double rms = sqrtl(value / (long double) this->_frame_size); - auto db = (long double) 20 * (log(rms) / log(10)); - db = max((long double) -192, min(db, (long double) 0)); - - auto percentage = (float) (100 + (db * 1.92f)); - return max(0.f, min(percentage, 100.f)); } \ No newline at end of file diff --git a/native/serverconnection/src/audio/filter/FilterThreshold.h b/native/serverconnection/src/audio/filter/FilterThreshold.h index 4e47093..5e19e01 100644 --- a/native/serverconnection/src/audio/filter/FilterThreshold.h +++ b/native/serverconnection/src/audio/filter/FilterThreshold.h @@ -14,20 +14,18 @@ namespace tc::audio::filter { bool initialize(std::string& /* error */, float /* threshold */, size_t /* margin frames */); bool process(const void* /* buffer */) override; - long double analyze(const void* /* buffer */, size_t /* channel */); - inline void set_threshold(float value) { this->_threshold = value; } [[nodiscard]] inline float threshold() const { return this->_threshold; } /* in seconds */ - inline float margin_release_time() { return (float) this->_margin_samples / (float) this->_sample_rate; } + [[nodiscard]] inline float margin_release_time() { return (float) this->_margin_samples / (float) this->_sample_rate; } inline void set_margin_release_time(float value) { this->_margin_samples = (size_t) ceil((float) this->_sample_rate * value); } - inline void attack_smooth(float value) { this->_attack_smooth = value; } [[nodiscard]] inline float attack_smooth() const { return this->_attack_smooth; } + inline void attack_smooth(float value) { this->_attack_smooth = value; } - inline void release_smooth(float value) { this->_release_smooth = value; } [[nodiscard]] inline float release_smooth() const { return this->_release_smooth; } + inline void release_smooth(float value) { this->_release_smooth = value; } std::function on_analyze; private: diff --git a/native/serverconnection/src/audio/processing/AudioVolume.cpp b/native/serverconnection/src/audio/processing/AudioVolume.cpp new file mode 100644 index 0000000..5413781 --- /dev/null +++ b/native/serverconnection/src/audio/processing/AudioVolume.cpp @@ -0,0 +1,71 @@ +// +// Created by WolverinDEV on 28/03/2021. +// + +#include +#include +#include "AudioVolume.h" + +using namespace tc; + +/* technique based on http://www.vttoth.com/CMS/index.php/technical-notes/68 */ +inline float merge_ab(float a, float b, float n) { + /* + * Form: A,B := [0;n] + * IF A <= n/2 AND B <= N/2 + * Z = (2 * A * B) / n + * ELSE + * Z = 2(A + B) - (2/n) * A * B - n + * + * For a range from 0 to 2: Z = 2(A + B) - AB - 2 + */ + + auto half_n = n / 2; + auto inv_half_n = 1 / half_n; + + if(a < half_n && b < half_n) { + return inv_half_n * a * b; + } + + return 2 * (a + b) - inv_half_n * a * b - n; +} + +constexpr static auto kMaxChannelCount{32}; +float audio::audio_buffer_level(const float *buffer, size_t channel_count, size_t sample_count) { + /* + * TODO: May more like this: + * https://source.chromium.org/chromium/chromium/src/+/master:third_party/webrtc/modules/audio_processing/rms_level.cc;l=30;drc=c6366b7101f99520b9203a884665e3d069523a6b;bpv=1;bpt=1 + */ + + if(channel_count > kMaxChannelCount) { + return 0; + } + + long double square_buffer[kMaxChannelCount]; + + auto buffer_ptr = buffer; + for(size_t sample{0}; sample < sample_count; sample++) { + for(size_t channel{0}; channel < channel_count; channel++) { + auto value = *buffer_ptr++; + square_buffer[channel] += value * value; + } + } + + float result{0}; + for(size_t channel{0}; channel < channel_count; channel++) { + long double rms = sqrtl(square_buffer[channel] / (long double) sample_count); + auto db = (long double) 20 * (log(rms) / log(10)); + db = std::max((long double) -192, std::min(db, (long double) 0)); + + auto percentage = (float) (100 + (db * 1.92f)); + + auto channel_value = std::clamp(percentage, 0.f, 100.f); + if(channel == 0) { + result = channel_value; + } else { + result = merge_ab(result, channel_value, 100); + } + } + + return result; +} \ No newline at end of file diff --git a/native/serverconnection/src/audio/processing/AudioVolume.h b/native/serverconnection/src/audio/processing/AudioVolume.h new file mode 100644 index 0000000..f5cd9ce --- /dev/null +++ b/native/serverconnection/src/audio/processing/AudioVolume.h @@ -0,0 +1,11 @@ +#pragma once + +namespace tc::audio { + /** + * @param buffer + * @param channel_count + * @param sample_count + * @return The audio energy in [0;100] + */ + float audio_buffer_level(const float *buffer /* buffer */, size_t /* channel count */, size_t /* sample count */); +} \ No newline at end of file