blob: 434c45bf5ecc62ec3005d5b0727e5a298c9074d8 [file] [log] [blame]
/*
* MdctEnc.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 "MdctEnc.hpp"
#include <cmath>
#include "BandIndexTables.hpp"
#include "MdctWindows.hpp"
namespace Lc3Enc {
MdctEnc::MdctEnc(const Lc3Config& lc3Config_)
: lc3Config(lc3Config_),
X(nullptr),
E_B(nullptr),
near_nyquist_flag(0),
dctIVDbl(lc3Config.NF),
skipMdct(0),
t(nullptr),
wN(nullptr),
I_fs(nullptr) {
X = dctIVDbl.out;
E_B = new double[lc3Config.N_b];
// initialization of E_B skipped since this will be fully re-computed on any
// run anyway
t = new int16_t[2 * lc3Config.NF];
for (uint16_t n = 0; n < 2 * lc3Config.NF; n++) {
t[n] = 0;
}
// Note: we do not add additional configuration error checking at this level.
// We assume that there will be nor processing with invalid configuration,
// thus nonsense results for invalid lc3Config.N_ms and/or lc3Config.Fs_ind
// are accepted here.
I_fs = &I_8000[0]; // default initialization to avoid warnings
wN = w_N80; // default initialization to avoid warnings
// Note: we do not add additional configuration error checking at this level.
// We assume that there will be nor processing with invalid configuration,
// thus nonsense results for invalid lc3Config.N_ms and/or lc3Config.Fs_ind
// are accepted here.
if (lc3Config.N_ms == Lc3Config::FrameDuration::d7p5ms) {
switch (lc3Config.Fs_ind) {
case 0:
I_fs = &I_8000_7p5ms[0];
wN = w_N60_7p5ms;
break;
case 1:
I_fs = &I_16000_7p5ms[0];
wN = w_N120_7p5ms;
break;
case 2:
I_fs = &I_24000_7p5ms[0];
wN = w_N180_7p5ms;
break;
case 3:
I_fs = &I_32000_7p5ms[0];
wN = w_N240_7p5ms;
break;
case 4:
I_fs = &I_48000_7p5ms[0];
wN = w_N360_7p5ms;
break;
}
} else {
// Lc3Config::FrameDuration::d10ms (and other as fallback)
switch (lc3Config.Fs_ind) {
case 0:
I_fs = &I_8000[0];
wN = w_N80;
break;
case 1:
I_fs = &I_16000[0];
wN = w_N160;
break;
case 2:
I_fs = &I_24000[0];
wN = w_N240;
break;
case 3:
I_fs = &I_32000[0];
wN = w_N320;
break;
case 4:
I_fs = &I_48000[0];
wN = w_N480;
break;
}
}
}
MdctEnc::~MdctEnc() { delete[] t; }
const int* MdctEnc::get_I_fs() const { return I_fs; }
void MdctEnc::MdctFastDbl(const double* const tw) {
for (uint16_t n = 0; n < lc3Config.NF / 2; n++) {
dctIVDbl.in[n] =
-tw[3 * lc3Config.NF / 2 - 1 - n] - tw[3 * lc3Config.NF / 2 + n];
}
for (uint16_t n = lc3Config.NF / 2; n < lc3Config.NF; n++) {
dctIVDbl.in[n] =
tw[n - lc3Config.NF / 2] - tw[3 * lc3Config.NF / 2 - 1 - n];
}
dctIVDbl.run();
double gain = 1.0 / sqrt(2.0 * lc3Config.NF);
for (uint16_t k = 0; k < lc3Config.NF; k++) {
dctIVDbl.out[k] *= gain;
}
}
void MdctEnc::run(const int16_t* const x_s) {
if (skipMdct) {
return;
}
// 3.3.4.2 Update time buffer (LC3 Specification d09r02_F2F)
// Note: specification has strange loop indices
// -> corrected start index appropriately
for (uint16_t n = 0; n < (lc3Config.NF - lc3Config.Z); n++) {
t[n] = t[lc3Config.NF + n];
}
for (uint16_t n = lc3Config.NF - lc3Config.Z;
n < (2 * lc3Config.NF - lc3Config.Z); n++) {
t[n] = x_s[lc3Config.Z - lc3Config.NF + n];
}
// 3.3.4.3 Time-Frequency Transformation (LC3 Specification d09r02_F2F)
double tw[2 * lc3Config.NF];
for (uint16_t n = 0; n < 2 * lc3Config.NF; n++) {
tw[n] = wN[n] * t[n];
}
MdctFastDbl(tw);
// 3.3.4.4 Energy estimation per band (d09r02_F2F)
for (uint8_t b = 0; b < lc3Config.N_b; b++) {
E_B[b] = 0.0;
uint16_t width = I_fs[b + 1] - I_fs[b];
for (uint16_t k = I_fs[b]; k < I_fs[b + 1]; k++) {
E_B[b] += (X[k] * X[k]) / width;
}
}
// 3.3.4.5 Near Nyquist Detector (LC3 Specification d1.0r03; Errata 15041)
if (lc3Config.Fs <= 32000) {
const uint16_t nn_idx = (lc3Config.N_ms == Lc3Config::FrameDuration::d7p5ms)
? lc3Config.N_b - 4
: lc3Config.N_b - 2;
double upperBandsEnergy = 0;
double lowerBandsEnergy = 0;
for (uint8_t n = 0; n < lc3Config.N_b; n++) {
if (n < nn_idx) {
lowerBandsEnergy += E_B[n];
} else {
upperBandsEnergy += E_B[n];
}
}
const double NN_thresh = 30;
near_nyquist_flag =
(upperBandsEnergy > NN_thresh * lowerBandsEnergy) ? 1 : 0;
} else {
near_nyquist_flag = 0;
}
}
void MdctEnc::registerDatapoints(DatapointContainer* datapoints) {
if (nullptr != datapoints) {
datapoints->addDatapoint("skipMdct", &skipMdct, sizeof(skipMdct));
datapoints->addDatapoint("X", &X[0], sizeof(double) * lc3Config.NF);
datapoints->addDatapoint("E_B", &E_B[0], sizeof(double) * lc3Config.N_b);
datapoints->addDatapoint("near_nyquist_flag", &near_nyquist_flag,
sizeof(near_nyquist_flag));
}
}
} // namespace Lc3Enc