Outsource the audio energy calculation

This commit is contained in:
WolverinDEV 2021-03-28 18:56:58 +02:00
parent 22d9059ad5
commit 347f1944b7
4 changed files with 88 additions and 63 deletions

View File

@ -2,7 +2,7 @@
#include <algorithm>
#include <cmath>
#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));
}

View File

@ -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<void(float)> on_analyze;
private:

View File

@ -0,0 +1,71 @@
//
// Created by WolverinDEV on 28/03/2021.
//
#include <cmath>
#include <algorithm>
#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;
}

View File

@ -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 */);
}