blob: ba56b5d5d9e641528d6f5c73ac36316771051328 [file] [log] [blame]
/*
* MdctDec.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 "MdctDec.hpp"
#include <cmath>
#include "MdctWindows.hpp"
namespace Lc3Dec {
MdctDec::MdctDec(const Lc3Config& lc3Config_)
: lc3Config(lc3Config_),
x_hat_mdct(nullptr),
dctIVDbl(lc3Config.NF),
mem_ola_add(nullptr),
t_hat_mdct(nullptr),
wN(nullptr) {
mem_ola_add = new double[lc3Config.NF - lc3Config.Z];
t_hat_mdct = new double[2 * lc3Config.NF];
x_hat_mdct = new double[lc3Config.NF];
for (uint16_t n = 0; n < (lc3Config.NF - lc3Config.Z); n++) {
mem_ola_add[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.
wN = w_N80; // default initialization to avoid warnings
if (lc3Config.N_ms == Lc3Config::FrameDuration::d7p5ms) {
switch (lc3Config.NF) {
case 60:
wN = w_N60_7p5ms;
break;
case 120:
wN = w_N120_7p5ms;
break;
case 180:
wN = w_N180_7p5ms;
break;
case 240:
wN = w_N240_7p5ms;
break;
case 360:
wN = w_N360_7p5ms;
break;
}
} else {
// Lc3Config::FrameDuration::d10ms (and other as fallback)
switch (lc3Config.NF) {
case 80:
wN = w_N80;
break;
case 160:
wN = w_N160;
break;
case 240:
wN = w_N240;
break;
case 320:
wN = w_N320;
break;
case 480:
wN = w_N480;
break;
}
}
}
MdctDec::~MdctDec() {
if (nullptr != mem_ola_add) {
delete[] mem_ola_add;
}
if (nullptr != t_hat_mdct) {
delete[] t_hat_mdct;
}
if (nullptr != x_hat_mdct) {
delete[] x_hat_mdct;
}
}
void MdctDec::MdctInvFastDbl() {
dctIVDbl.run();
for (uint16_t n = 0; n < lc3Config.NF; n++) {
t_hat_mdct[n] = dctIVDbl.out[n];
}
for (uint16_t n = lc3Config.NF; n < 2 * lc3Config.NF; n++) {
t_hat_mdct[n] = -dctIVDbl.out[2 * lc3Config.NF - 1 - n];
}
// TODO try to optimize out the mem-buffer
double mem[lc3Config.NF / 2];
for (uint16_t n = 0; n < lc3Config.NF / 2; n++) {
mem[n] = t_hat_mdct[n];
}
for (uint16_t n = 0; n < 3 * lc3Config.NF / 2; n++) {
t_hat_mdct[n] = t_hat_mdct[n + lc3Config.NF / 2];
}
for (uint16_t n = 3 * lc3Config.NF / 2; n < 2 * lc3Config.NF; n++) {
t_hat_mdct[n] = -mem[n - 3 * lc3Config.NF / 2];
}
double gain = 1.0 / sqrt(2.0 * lc3Config.NF);
for (uint16_t n = 0; n < 2 * lc3Config.NF; n++) {
t_hat_mdct[n] *= gain;
}
}
void MdctDec::run(const double* const X_hat) {
if (!lc3Config.isValid()) {
return;
}
for (uint16_t k = 0; k < lc3Config.NE; k++) {
dctIVDbl.in[k] = X_hat[k];
}
for (uint16_t k = lc3Config.NE; k < lc3Config.NF; k++) {
dctIVDbl.in[k] = 0;
}
// 1. Generation of time domain aliasing buffer
MdctInvFastDbl();
// 2. Windowing of time-aliased buffer
for (uint16_t n = 0; n < 2 * lc3Config.NF; n++) {
t_hat_mdct[n] *= wN[2 * lc3Config.NF - 1 - n];
}
// 3. Conduct overlapp-add operation
for (uint16_t n = 0; n < lc3Config.NF - lc3Config.Z; n++) {
x_hat_mdct[n] = mem_ola_add[n] + t_hat_mdct[lc3Config.Z + n];
mem_ola_add[n] = t_hat_mdct[lc3Config.NF + lc3Config.Z + n];
}
for (uint16_t n = lc3Config.NF - lc3Config.Z; n < lc3Config.NF; n++) {
x_hat_mdct[n] = t_hat_mdct[lc3Config.Z + n];
}
}
void MdctDec::registerDatapoints(DatapointContainer* datapoints) {
if (nullptr != datapoints) {
datapoints->addDatapoint("X_hat_mdct", dctIVDbl.in,
sizeof(double) * lc3Config.NF);
datapoints->addDatapoint("t_hat_mdct", t_hat_mdct,
sizeof(double) * 2 * lc3Config.NF);
datapoints->addDatapoint("mem_ola_add", mem_ola_add,
sizeof(double) * (lc3Config.NF - lc3Config.Z));
datapoints->addDatapoint("x_hat_mdct", x_hat_mdct,
sizeof(double) * lc3Config.NF);
}
}
} // namespace Lc3Dec