blob: 94dbd2816eb6db37970da8299072b9e90052ff80 [file] [log] [blame]
/*
* AttackDetector.cpp
*
* Copyright 2021 HIMSA II K/S - www.himsa.com. Represented by EHIMA -
* www.ehima.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AttackDetector.hpp"
#include <cmath>
namespace Lc3Enc {
AttackDetector::AttackDetector(const Lc3Config& lc3Config_)
: lc3Config(lc3Config_),
M_F((lc3Config.N_ms == Lc3Config::FrameDuration::d10ms)
? 160
: 120), // 16*N_ms
F_att(0),
E_att_last(0),
A_att_last(0),
P_att_last(-1) {
// make sure these states are initially zero as demanded by the specification
x_att_last[0] = 0;
x_att_last[1] = 0;
}
AttackDetector::~AttackDetector() {}
void AttackDetector::run(const int16_t* const x_s, uint16_t nbytes) {
// 3.3.6.1 Overview (d09r06_FhG)
// -> attack detection active only for higher bitrates and fs>=32000;
// otherwise defaults are set
F_att = 0;
if (lc3Config.Fs < 32000) {
return;
}
bool isActive =
((lc3Config.N_ms == Lc3Config::FrameDuration::d10ms) &&
(lc3Config.Fs == 32000) && (nbytes > 80)) ||
((lc3Config.N_ms == Lc3Config::FrameDuration::d10ms) &&
(lc3Config.Fs >= 44100) && (nbytes >= 100)) ||
((lc3Config.N_ms == Lc3Config::FrameDuration::d7p5ms) &&
(lc3Config.Fs == 32000) && (nbytes >= 61) && (nbytes < 150)) ||
((lc3Config.N_ms == Lc3Config::FrameDuration::d7p5ms) &&
(lc3Config.Fs >= 44100) && (nbytes >= 75) && (nbytes < 150));
if (!isActive) {
// Note: in bitrate switching situations we have to set proper states
E_att_last = 0;
A_att_last = 0;
P_att_last = -1;
return;
}
// 3.3.6.2 Downsampling and filtering of input signal (d09r02_F2F)
// Note: the following section might be converted to int32 instead
// of double computation (maybe something for optimization)
double x_att_extended[M_F + 2];
x_att_extended[0] = x_att_last[0];
x_att_extended[1] = x_att_last[1];
double* x_att = &x_att_extended[2];
for (uint8_t n = 0; n < M_F; n++) // downsampling
{
x_att[n] = 0;
for (uint8_t m = 0; m < lc3Config.NF / M_F; m++) {
x_att[n] += x_s[lc3Config.NF / M_F * n + m];
}
}
x_att_last[0] = x_att[M_F - 2];
x_att_last[1] = x_att[M_F - 1];
double* x_hp = x_att_extended; // just for improve readability
for (uint8_t n = 0; n < M_F; n++) // highpass-filtering (in-place!)
{
x_hp[n] = 0.375 * x_att[n] - 0.5 * x_att[n - 1] + 0.125 * x_att[n - 2];
}
// 3.3.6.3 Energy calculation & 3.3.6.4 Attack detection (d09r06_FhG)
int8_t P_att = -1;
const uint8_t N_blocks =
(lc3Config.N_ms == Lc3Config::FrameDuration::d10ms) ? 4 : 3; // N_ms/2.5
for (uint8_t n = 0; n < N_blocks; n++) {
double E_att = 0;
for (uint8_t l = 40 * n; l <= (40 * n + 39); l++) {
E_att += x_hp[l] * x_hp[l];
}
double A_att =
(0.25 * A_att_last > E_att_last) ? 0.25 * A_att_last : E_att_last;
if (E_att > 8.5 * A_att) {
// attack detected
P_att = n;
}
E_att_last = E_att;
A_att_last = A_att;
}
const uint8_t T_att = (lc3Config.N_ms == Lc3Config::FrameDuration::d10ms)
? 2
: 1; // floor(N_blocks/2)
F_att = (P_att >= 0) || (P_att_last >= T_att);
P_att_last = P_att; // prepare next frame
}
void AttackDetector::registerDatapoints(DatapointContainer* datapoints) {
if (nullptr != datapoints) {
datapoints->addDatapoint("F_att", &F_att, sizeof(F_att));
}
}
} // namespace Lc3Enc