blob: 4f6552d48a3630e8f663a08e800a1e1dd70530e7 [file] [log] [blame]
/*
* SpectralQuantization.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 "SpectralQuantization.hpp"
#include <algorithm>
#include <cmath>
#include "SpectralDataTables.hpp"
namespace Lc3Enc {
SpectralQuantization::SpectralQuantization(uint16_t NE_, uint8_t fs_ind_)
: NE(NE_),
fs_ind(fs_ind_),
X_q(nullptr),
lastnz(0),
nbits_trunc(0),
rateFlag(0),
lsbMode(0),
gg(0),
lastnz_trunc(0),
gg_ind(0),
datapoints(nullptr),
gg_off(0),
gg_min(0),
nbits_offset(0),
nbits_spec(0),
nbits_spec_adj(0),
nbits_est(0),
reset_offset_old(false),
nbits_offset_old(0),
nbits_spec_old(0),
nbits_est_old(0)
{
X_q = new int16_t[NE];
}
SpectralQuantization::~SpectralQuantization() { delete[] X_q; }
void SpectralQuantization::updateGlobalGainEstimationParameter(
uint16_t nbits, uint16_t nbits_spec_local) {
if (reset_offset_old) {
nbits_offset = 0;
} else {
nbits_offset =
0.8 * nbits_offset_old +
0.2 * std::min(40.f, std::max(-40.f, nbits_offset_old + nbits_spec_old -
nbits_est_old));
}
nbits_spec_adj = static_cast<uint16_t>(nbits_spec_local + nbits_offset + 0.5);
gg_off = -std::min(115, nbits / (10 * (fs_ind + 1))) - 105 - 5 * (fs_ind + 1);
}
void SpectralQuantization::computeSpectralEnergy(double* E, const double* X_f) {
for (uint16_t k = 0; k < NE / 4; k++) {
E[k] = pow(2, -31);
for (uint8_t n = 0; n <= 3; n++) {
E[k] += X_f[4 * k + n] * X_f[4 * k + n];
}
E[k] = 10 * log10(E[k]);
}
}
void SpectralQuantization::globalGainEstimation(const double* E) {
// converted pseudo-code from page 49/50 (d09r02_F2F)
int16_t fac = 256;
//𝑔𝑔𝑖𝑛𝑑 = 255;
gg_ind = 255;
for (uint8_t iter = 0; iter < 8; iter++) {
fac >>= 1;
//𝑔𝑔𝑖𝑛𝑑 -= fac;
gg_ind -= fac;
double tmp = 0;
uint8_t iszero = 1;
// for (i = 𝑁𝐸/4-1; i >= 0; i--)
for (int8_t i = NE / 4 - 1; i >= 0; i--) {
// if (E[i]*28/20 < (𝑔𝑔𝑖𝑛𝑑+𝑔𝑔𝑜𝑓𝑓))
if (E[i] * 28 / 20.0 < (gg_ind + gg_off)) {
if (iszero == 0) {
tmp += 2.7 * 28 / 20.0;
}
} else {
// if ((𝑔𝑔𝑖𝑛𝑑+𝑔𝑔𝑜𝑓𝑓) < E[i]*28/20 - 43*28/20)
if ((gg_ind + gg_off) < E[i] * 28 / 20.0 - 43 * 28 / 20.0) {
// tmp += 2*E[i]*28/20 – 2*(𝑔𝑔𝑖𝑛𝑑+𝑔𝑔𝑜𝑓𝑓) - 36*28/20;
tmp += 2 * E[i] * 28 / 20.0 - 2 * (gg_ind + gg_off) - 36 * 28 / 20.0;
} else {
// tmp += E[i]*28/20 – (𝑔𝑔𝑖𝑛𝑑+𝑔𝑔𝑜𝑓𝑓) + 7*28/20;
tmp += E[i] * 28 / 20.0 - (gg_ind + gg_off) + 7 * 28 / 20.0;
}
iszero = 0;
}
}
// if (tmp > 𝑛𝑏𝑖𝑡𝑠𝑠𝑝𝑒𝑐′ *1.4*28/20 && iszero == 0)
if ((tmp > nbits_spec_adj * 1.4 * 28 / 20.0) && (iszero == 0)) {
//𝑔𝑔𝑖𝑛𝑑 += fac;
gg_ind += fac;
}
}
}
bool SpectralQuantization::globalGainLimitation(const double* const X_f) {
// -> not precisely clear where this limitation should occur
// -> Is the following converted pseudo code meant?
// -> Are the chosen datatypes appropriate?
double X_f_max = 0;
for (uint16_t n = 0; n < NE; n++) {
X_f_max = std::max(X_f_max, fabs(X_f[n]));
}
if (X_f_max > 0) {
gg_min = ceil(28 * log10(X_f_max / (32768 - 0.375))) - gg_off;
} else {
gg_min = 0;
}
bool reset_offset = false;
// if (𝑔𝑔𝑖𝑛𝑑 < 𝑔𝑔𝑚𝑖𝑛 || 𝑋𝑓𝑚𝑎𝑥 == 0)
if ((gg_ind < gg_min) || X_f_max == 0) {
//𝑔𝑔𝑖𝑛𝑑 = 𝑔𝑔𝑚𝑖𝑛;
//𝑟𝑒𝑠𝑒𝑡𝑜𝑓𝑓𝑠𝑒𝑡 = 1;
gg_ind = gg_min;
reset_offset = true;
} else {
//𝑟𝑒𝑠𝑒𝑡𝑜𝑓𝑓𝑠𝑒𝑡 = 0;
reset_offset = false;
}
return reset_offset;
}
void SpectralQuantization::quantizeSpectrum(const double* const X_f) {
gg = pow(10.f, (gg_ind + gg_off) / 28.0f);
for (uint16_t n = 0; n < NE; n++) {
if (X_f[n] >= 0) {
X_q[n] = X_f[n] / gg + 0.375;
} else {
X_q[n] = X_f[n] / gg - 0.375;
}
}
}
void SpectralQuantization::computeBitConsumption(uint16_t nbits,
uint8_t& modeFlag) {
// if (nbits > (160 + 𝑓𝑠𝑖𝑛𝑑 * 160))
if (nbits > (160 + fs_ind * 160)) {
rateFlag = 512;
} else {
rateFlag = 0;
}
// if (nbits >= (480 + 𝑓𝑠𝑖𝑛𝑑 * 160))
if (nbits >= (480 + fs_ind * 160)) {
modeFlag = 1;
} else {
modeFlag = 0;
}
// lastnz = 𝑁𝐸;
lastnz = NE;
// while (lastnz>2 && 𝑋𝑞[lastnz-1] == 0 && 𝑋𝑞[lastnz-2] == 0)
while ((lastnz > 2) && (X_q[lastnz - 1] == 0) && (X_q[lastnz - 2] == 0)) {
lastnz -= 2;
}
//𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 = 0;
//𝑛𝑏𝑖𝑡𝑠𝑡𝑟𝑢𝑛𝑐 = 0;
//𝑛𝑏𝑖𝑡𝑠𝑙𝑠𝑏 = 0;
uint32_t nbits_est_local = 0;
uint32_t nbits_trunc_local = 0;
nbits_lsb = 0;
lastnz_trunc = 2;
uint16_t c = 0;
for (uint16_t n = 0; n < lastnz; n = n + 2) {
uint16_t t = c + rateFlag;
// if (n > 𝑁𝐸/2)
if (n > NE / 2) {
t += 256;
}
// a = abs(𝑋𝑞[n]);
uint16_t a = abs(X_q[n]);
uint16_t a_lsb = a;
// b = abs(𝑋𝑞[n+1]);
uint16_t b = abs(X_q[n + 1]);
uint16_t b_lsb = b;
uint16_t lev = 0;
// while (max(a,b) >= 4)
uint8_t pki;
while (std::max(a, b) >= 4) {
pki = ac_spec_lookup[t + lev * 1024];
//𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 += ac_spec_bits[pki][16];
nbits_est_local += ac_spec_bits[pki][16];
if ((lev == 0) && (modeFlag == 1)) {
//𝑛𝑏𝑖𝑡𝑠𝑙𝑠𝑏 += 2;
nbits_lsb += 2;
} else {
//𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 += 2*2048;
nbits_est_local += 2 * 2048;
}
a >>= 1;
b >>= 1;
lev = std::min(lev + 1, 3);
}
pki = ac_spec_lookup[t + lev * 1024];
uint16_t sym = a + 4 * b;
//𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 += ac_spec_bits[pki][sym];
nbits_est_local += ac_spec_bits[pki][sym];
// a_lsb = abs(𝑋𝑞[n]); -> implemented earlier
// b_lsb = abs(𝑋𝑞[n+1]); -> implemented earlier
//𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 += (min(a_lsb,1) + min(b_lsb,1)) * 2048;
// nbits_est_local += (std::min(a_lsb,static_cast<uint16_t>(1))
// + std::min(b_lsb,static_cast<uint16_t>(1))) * 2048;
// alternative implementation (more clear, more performant?)
if (a_lsb > 0) nbits_est_local += 2048;
if (b_lsb > 0) nbits_est_local += 2048;
if ((lev > 0) && (modeFlag == 1)) {
a_lsb >>= 1;
b_lsb >>= 1;
// if (a_lsb == 0 && 𝑋𝑞[n] != 0)
if (a_lsb == 0 && X_q[n] != 0) {
//𝑛𝑏𝑖𝑡𝑠𝑙𝑠𝑏++;
nbits_lsb++;
}
// if (b_lsb == 0 && 𝑋𝑞[n+1] != 0)
if (b_lsb == 0 && X_q[n + 1] != 0) {
//𝑛𝑏𝑖𝑡𝑠𝑙𝑠𝑏++;
nbits_lsb++;
}
}
// if ((𝑋𝑞[n] != 0 || 𝑋𝑞[n+1] != 0) && (𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 <= 𝑛𝑏𝑖𝑡𝑠𝑠𝑝𝑒𝑐*2048))
// if (((X_q[n] != 0) || (X_q[n+1] != 0)) && (nbits_est_local <=
// nbits_spec*2048))
if (((X_q[n] != 0) || (X_q[n + 1] != 0)) &&
(ceil(nbits_est_local / 2048.0) <= nbits_spec)) {
lastnz_trunc = n + 2;
//𝑛𝑏𝑖𝑡𝑠𝑡𝑟𝑢𝑛𝑐 = 𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡;
nbits_trunc_local = nbits_est_local;
}
if (lev <= 1) {
t = 1 + (a + b) * (lev + 1);
} else {
t = 12 + lev;
}
c = (c & 15) * 16 + t;
}
//𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 = ceil(𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡/2048) + 𝑛𝑏𝑖𝑡𝑠𝑙𝑠𝑏;
nbits_est = ceil(nbits_est_local / 2048.0) + nbits_lsb;
//𝑛𝑏𝑖𝑡𝑠𝑡𝑟𝑢𝑛𝑐 = ceil(𝑛𝑏𝑖𝑡𝑠𝑡𝑟𝑢𝑛𝑐/2048);
nbits_trunc = ceil(nbits_trunc_local / 2048.0);
}
bool SpectralQuantization::globalGainAdjustment() {
uint16_t t1[5] = {80, 230, 380, 530, 680};
uint16_t t2[5] = {500, 1025, 1550, 2075, 2600};
uint16_t t3[5] = {850, 1700, 2550, 3400, 4250};
double delta;
double delta2;
// if (𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 < t1[𝑓𝑠𝑖𝑛𝑑])
if (nbits_est < t1[fs_ind]) {
// delta = (𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡+48)/16;
delta = (nbits_est + 48) / static_cast<double>(16);
}
// else if (𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 < t2[𝑓𝑠𝑖𝑛𝑑])
else if (nbits_est < t2[fs_ind]) {
// tmp1 = t1[𝑓𝑠𝑖𝑛𝑑]/16+3;
// tmp2 = t2[𝑓𝑠𝑖𝑛𝑑]/48;
// delta = (𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡-t1[𝑓𝑠𝑖𝑛𝑑])*(tmp2-tmp1)/(t2[𝑓𝑠𝑖𝑛𝑑]-t1[𝑓𝑠𝑖𝑛𝑑]) + tmp1;
double tmp1 = t1[fs_ind] / static_cast<double>(16) + 3;
double tmp2 = t2[fs_ind] / static_cast<double>(48);
delta =
(nbits_est - t1[fs_ind]) * (tmp2 - tmp1) / (t2[fs_ind] - t1[fs_ind]) +
tmp1;
}
// else if (𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 < t3[𝑓𝑠𝑖𝑛𝑑])
else if (nbits_est < t3[fs_ind]) {
// delta = 𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡/48;
delta = nbits_est / static_cast<double>(48);
} else {
// delta = t3[𝑓𝑠𝑖𝑛𝑑]/48;
delta = t3[fs_ind] / static_cast<double>(48);
}
// delta = nint(delta); // this looks like we need floating point
// nint(.) is the rounding-to-nearest-integer function.
delta = static_cast<int16_t>(delta +
0.5); // this looks like we need floating point
delta2 = delta + 2;
uint16_t gg_ind_origin = gg_ind;
/*
if ((𝑔𝑔𝑖𝑛𝑑 < 255 && 𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 > 𝑛𝑏𝑖𝑡𝑠𝑠𝑝𝑒𝑐) ||
(𝑔𝑔𝑖𝑛𝑑 > 0 && 𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 < 𝑛𝑏𝑖𝑡𝑠𝑠𝑝𝑒𝑐 – delta2))
{
if (𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 < 𝑛𝑏𝑖𝑡𝑠𝑠𝑝𝑒𝑐 – delta2)
{
𝑔𝑔𝑖𝑛𝑑 -= 1;
}
else if (𝑔𝑔𝑖𝑛𝑑 == 254 || 𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 < 𝑛𝑏𝑖𝑡𝑠𝑠𝑝𝑒𝑐 + delta)
{
𝑔𝑔𝑖𝑛𝑑 += 1;
}
else
{
𝑔𝑔𝑖𝑛𝑑 += 2;
}
𝑔𝑔𝑖𝑛𝑑 = max(𝑔𝑔𝑖𝑛𝑑, 𝑔𝑔𝑚𝑖𝑛);
}
*/
if (((gg_ind < 255) && (nbits_est > nbits_spec)) ||
((gg_ind > 0) && (nbits_est < nbits_spec - delta2))) {
if (nbits_est < nbits_spec - delta2) {
gg_ind -= 1;
} else if ((gg_ind == 254) || (nbits_est < nbits_spec + delta)) {
gg_ind += 1;
} else {
gg_ind += 2;
}
gg_ind = std::max(gg_ind, gg_min);
}
return (gg_ind_origin != gg_ind);
}
void SpectralQuantization::run(const double* const X_f, uint16_t nbits,
uint16_t nbits_spec_local) {
nbits_spec = nbits_spec_local;
// 3.3.10.2 First global gain estimation (d09r02_F2F)
updateGlobalGainEstimationParameter(nbits, nbits_spec_local);
double E[NE / 4];
computeSpectralEnergy(E, X_f);
globalGainEstimation(E);
// Finally, the quantized gain index is limited such that
// the quantized spectrum stays within the range [-32768,32767]
bool reset_offset = globalGainLimitation(X_f);
// 3.3.10.3 Quantization (d09r02_F2F)
quantizeSpectrum(X_f);
// 3.3.10.4 Bit consumption (d09r02_F2F)
uint8_t modeFlag;
computeBitConsumption(nbits, modeFlag);
if (nullptr != datapoints) {
datapoints->log("gg_ind", &gg_ind, sizeof(gg_ind));
datapoints->log("gg", &gg, sizeof(gg));
datapoints->log("X_q", X_q, sizeof(int16_t) * NE);
datapoints->log("lastnz", &lastnz, sizeof(lastnz));
datapoints->log("lastnz_trunc", &lastnz_trunc, sizeof(lastnz_trunc));
datapoints->log("nbits_est", &nbits_est, sizeof(nbits_est));
datapoints->log("nbits_trunc", &nbits_trunc, sizeof(nbits_trunc));
}
// 3.3.10.5 Truncation (d09r02_F2F)
for (uint16_t k = lastnz_trunc; k < lastnz; k++) {
//𝑋𝑞[k] = 0;
X_q[k] = 0;
}
// if (modeFlag == 1 && 𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 > 𝑛𝑏𝑖𝑡𝑠𝑠𝑝𝑒𝑐)
if ((modeFlag == 1) && (nbits_est > nbits_spec)) {
lsbMode = 1;
} else {
lsbMode = 0;
}
if (nullptr != datapoints) {
datapoints->log("lsbMode", &lsbMode, sizeof(lsbMode));
}
// Note: states needs to be transferred prior
// to spectrum re-quantization!
nbits_spec_old = nbits_spec;
nbits_est_old = nbits_est;
reset_offset_old = reset_offset;
nbits_offset_old = nbits_offset;
// 3.3.10.6 Global gain adjustment (d09r02_F2F)
bool isAdjusted = globalGainAdjustment();
if (isAdjusted) {
quantizeSpectrum(X_f);
computeBitConsumption(nbits, modeFlag);
// 3.3.10.5 Truncation (d09r02_F2F)
for (uint16_t k = lastnz_trunc; k < lastnz; k++) {
//𝑋𝑞[k] = 0;
X_q[k] = 0;
}
// if (modeFlag == 1 && 𝑛𝑏𝑖𝑡𝑠𝑒𝑠𝑡 > 𝑛𝑏𝑖𝑡𝑠𝑠𝑝𝑒𝑐)
if (modeFlag == 1 && nbits_est > nbits_spec) {
lsbMode = 1;
} else {
lsbMode = 0;
}
}
if (nullptr != datapoints) {
datapoints->log("isAdjusted", &isAdjusted, sizeof(isAdjusted));
datapoints->log("gg_ind_adj", &gg_ind, sizeof(gg_ind));
datapoints->log("gg_adj", &gg, sizeof(gg));
datapoints->log("X_q_req", X_q, sizeof(int16_t) * NE);
datapoints->log("lastnz_req", &lastnz_trunc, sizeof(lastnz_trunc));
datapoints->log("nbits_est_req", &nbits_est, sizeof(nbits_est));
datapoints->log("nbits_trunc_req", &nbits_trunc, sizeof(nbits_trunc));
datapoints->log("lsbMode_req", &lsbMode, sizeof(lsbMode));
}
}
void SpectralQuantization::registerDatapoints(DatapointContainer* datapoints_) {
datapoints = datapoints_;
if (nullptr != datapoints) {
datapoints->addDatapoint("gg_off", &gg_off, sizeof(gg_off));
datapoints->addDatapoint("gg_min", &gg_min, sizeof(gg_min));
datapoints->addDatapoint("nbits_offset", &nbits_offset,
sizeof(nbits_offset));
}
}
} // namespace Lc3Enc