266 lines
8.6 KiB
C++
266 lines
8.6 KiB
C++
/*
|
||
* EncoderFrame.cpp
|
||
*
|
||
* Copyright 2019 HIMSA II K/S - www.himsa.dk. 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 "EncoderFrame.hpp"
|
||
|
||
#include <cstring>
|
||
#include <cmath>
|
||
|
||
namespace Lc3Enc
|
||
{
|
||
|
||
EncoderFrame::EncoderFrame(
|
||
MdctEnc& mdctEnc_,
|
||
AttackDetector& attackDetector_,
|
||
SpectralNoiseShaping& spectralNoiseShaping_,
|
||
TemporalNoiseShaping& temporalNoiseShaping_,
|
||
SpectralQuantization& spectralQuantization_,
|
||
NoiseLevelEstimation& noiseLevelEstimation_,
|
||
const Lc3Config& lc3Config_,
|
||
uint16_t nbytes_) :
|
||
lc3Config(lc3Config_),
|
||
nbytes(nbytes_),
|
||
datapoints(nullptr),
|
||
mdctEnc(mdctEnc_),
|
||
bandwidthDetector(lc3Config),
|
||
attackDetector(attackDetector_),
|
||
spectralNoiseShaping(spectralNoiseShaping_),
|
||
temporalNoiseShaping(temporalNoiseShaping_),
|
||
longTermPostfilter(lc3Config),
|
||
spectralQuantization(spectralQuantization_),
|
||
noiseLevelEstimation(noiseLevelEstimation_),
|
||
bitstreamEncoding(lc3Config.NE, nbytes),
|
||
frameN(0)
|
||
{
|
||
}
|
||
|
||
EncoderFrame::~EncoderFrame()
|
||
{
|
||
}
|
||
|
||
void EncoderFrame::linkPreviousFrame(EncoderFrame* previousFrame)
|
||
{
|
||
if (NULL != previousFrame)
|
||
{
|
||
longTermPostfilter = previousFrame->longTermPostfilter;
|
||
this->frameN = previousFrame->frameN;
|
||
}
|
||
}
|
||
|
||
void EncoderFrame::run(const int16_t* x_s, uint8_t* bytes)
|
||
{
|
||
// increment frame counter
|
||
frameN++;
|
||
|
||
|
||
// 3.3.3 Input signal scaling (d09r02_F2F)
|
||
// -> should be made in calling code due to different data types being needed
|
||
|
||
//3.3.4 Low delay MDCT analysis (d09r02_F2F)
|
||
mdctEnc.run(x_s);
|
||
|
||
//3.3.5 Bandwidth detector (d09r02_F2F)
|
||
bandwidthDetector.run(mdctEnc.E_B);
|
||
|
||
//3.3.6 Time domain attack detector (d09r02_F2F)
|
||
attackDetector.run(x_s, nbytes);
|
||
|
||
//3.3.7 Spectral Noise Shaping (SNS) (d09r02_F2F)
|
||
spectralNoiseShaping.run(mdctEnc.X, mdctEnc.E_B, attackDetector.F_att);
|
||
|
||
//3.3.8 Temporal Noise Shaping (TNS) (d09r02_F2F)
|
||
const uint16_t nbits = nbytes * 8;
|
||
temporalNoiseShaping.run(spectralNoiseShaping.X_S, bandwidthDetector.P_bw, nbits);
|
||
|
||
//3.3.9 Long Term Postfilter (d09r02_F2F)
|
||
longTermPostfilter.run(x_s, nbits); // nbits needed to compensate error in specification (d09r08) -> needs to be consistent with reference application; spec. will be changed later
|
||
|
||
|
||
//3.3.10 Spectral quantization (d09r02_F2F)
|
||
//3.3.10.1 Bit budget (d09r02_F2F)
|
||
uint16_t nbits_ari = ceil(log2(lc3Config.NE/2));
|
||
if ( nbits <= 1280 )
|
||
{
|
||
nbits_ari += 3;
|
||
}
|
||
else if ( nbits <= 2560 )
|
||
{
|
||
nbits_ari += 4;
|
||
}
|
||
else
|
||
{
|
||
nbits_ari += 5;
|
||
}
|
||
|
||
const uint8_t nbits_gain = 8;
|
||
const uint8_t nbits_nf = 3;
|
||
uint16_t nbits_spec = nbits - (
|
||
bandwidthDetector.nbits_bw +
|
||
temporalNoiseShaping.nbits_TNS +
|
||
longTermPostfilter.nbits_LTPF +
|
||
spectralNoiseShaping.nbits_SNS +
|
||
nbits_gain +
|
||
nbits_nf +
|
||
nbits_ari
|
||
);
|
||
spectralQuantization.run(temporalNoiseShaping.X_f, nbits, nbits_spec);
|
||
|
||
// 3.3.11 Residual coding (d09r02_F2F)
|
||
// nbits_residual_max = 𝑛𝑏𝑖𝑡𝑠𝑠𝑝𝑒𝑐 - 𝑛𝑏𝑖𝑡𝑠𝑡𝑟𝑢𝑛𝑐 + 4;
|
||
int16_t nbits_residual_max = nbits_spec
|
||
- spectralQuantization.nbits_trunc + 4;
|
||
if (nbits_residual_max < 0)
|
||
{
|
||
nbits_residual_max = 0;
|
||
}
|
||
uint16_t nbits_residual = 0;
|
||
uint8_t res_bits[nbits_residual_max]; // TODO check whether the degenerated case with nbits_residual_max==0 works
|
||
if (nbits_residual_max > 0)
|
||
{// converted pseudo-code from page 54 (d09r02_F2F)
|
||
uint16_t k = 0;
|
||
while ( (k < spectralQuantization.NE) && (nbits_residual < nbits_residual_max) )
|
||
{
|
||
//if (𝑋𝑞[k]!= 0)
|
||
if (spectralQuantization.X_q[k]!= 0)
|
||
{
|
||
//if (𝑋𝑓[k] >= 𝑋𝑞[k]*gg)
|
||
if (temporalNoiseShaping.X_f[k] >=
|
||
spectralQuantization.X_q[k]*spectralQuantization.gg)
|
||
{
|
||
res_bits[nbits_residual] = 1;
|
||
}
|
||
else
|
||
{
|
||
res_bits[nbits_residual] = 0;
|
||
}
|
||
nbits_residual++;
|
||
}
|
||
k++;
|
||
}
|
||
}
|
||
if (nullptr!=datapoints)
|
||
{
|
||
datapoints->log("res_bits", &res_bits, sizeof(uint8_t)*nbits_residual_max);
|
||
}
|
||
|
||
// 3.3.12 Noise level estimation (d09r02_F2F)
|
||
noiseLevelEstimation.run(
|
||
temporalNoiseShaping.X_f,
|
||
spectralQuantization.X_q,
|
||
bandwidthDetector.P_bw,
|
||
spectralQuantization.gg);
|
||
|
||
|
||
// 3.3.13 Bitstream encoding (d09r02_F2F)
|
||
// 3.3.13.2 Initialization (d09r02_F2F)
|
||
bitstreamEncoding.init(bytes);
|
||
// 3.3.13.3 Side information (d09r02_F2F)
|
||
/* Bandwidth */
|
||
bitstreamEncoding.bandwidth(bandwidthDetector.P_bw, bandwidthDetector.nbits_bw);
|
||
/* Last non-zero tuple */
|
||
bitstreamEncoding.lastNonzeroTuple(spectralQuantization.lastnz_trunc);
|
||
/* LSB mode bit */
|
||
bitstreamEncoding.lsbModeBit(spectralQuantization.lsbMode);
|
||
/* Global Gain */
|
||
bitstreamEncoding.globalGain(spectralQuantization.gg_ind);
|
||
/* TNS activation flag */
|
||
bitstreamEncoding.tnsActivationFlag(
|
||
temporalNoiseShaping.num_tns_filters,
|
||
temporalNoiseShaping.rc_order
|
||
);
|
||
/* Pitch present flag */
|
||
bitstreamEncoding.pitchPresentFlag(longTermPostfilter.pitch_present);
|
||
/* Encode SCF VQ parameters - 1st stage (10 bits) */
|
||
bitstreamEncoding.encodeScfVq1stStage(
|
||
spectralNoiseShaping.get_ind_LF(),
|
||
spectralNoiseShaping.get_ind_HF()
|
||
);
|
||
/* Encode SCF VQ parameters - 2nd stage side-info (3-4 bits) */
|
||
/* Encode SCF VQ parameters - 2nd stage MPVQ data */
|
||
bitstreamEncoding.encodeScfVq2ndStage(
|
||
spectralNoiseShaping.get_shape_j(),
|
||
spectralNoiseShaping.get_Gind(),
|
||
spectralNoiseShaping.get_LS_indA(),
|
||
spectralNoiseShaping.get_index_joint_j()
|
||
);
|
||
/* LTPF data */
|
||
if (longTermPostfilter.pitch_present != 0)
|
||
{
|
||
bitstreamEncoding.ltpfData(
|
||
longTermPostfilter.ltpf_active,
|
||
longTermPostfilter.pitch_index
|
||
);
|
||
}
|
||
/* Noise Factor */
|
||
bitstreamEncoding.noiseFactor(noiseLevelEstimation.F_NF);
|
||
|
||
if (nullptr!=datapoints)
|
||
{
|
||
datapoints->log("bytes_side_info", &bytes[0], sizeof(uint8_t)*nbytes);
|
||
}
|
||
|
||
// 3.3.13.4 Arithmetic encoding (d09r02_F2F)
|
||
/* Arithmetic Encoder Initialization */
|
||
bitstreamEncoding.ac_enc_init();
|
||
/* TNS data */
|
||
bitstreamEncoding.tnsData(
|
||
temporalNoiseShaping.tns_lpc_weighting,
|
||
temporalNoiseShaping.num_tns_filters,
|
||
temporalNoiseShaping.rc_order,
|
||
temporalNoiseShaping.rc_i
|
||
);
|
||
/* Spectral data */
|
||
uint8_t lsbs[spectralQuantization.nbits_lsb]; // TODO check whether the degenerated case with nlsbs==0 works
|
||
bitstreamEncoding.spectralData(
|
||
spectralQuantization.lastnz_trunc,
|
||
spectralQuantization.rateFlag,
|
||
spectralQuantization.lsbMode,
|
||
spectralQuantization.X_q,
|
||
spectralQuantization.nbits_lsb,
|
||
lsbs
|
||
);
|
||
|
||
// 3.3.13.5 Residual data and finalization (d09r02_F2F)
|
||
bitstreamEncoding.residualDataAndFinalization(
|
||
spectralQuantization.lsbMode,
|
||
nbits_residual,
|
||
res_bits
|
||
);
|
||
|
||
|
||
if (nullptr!=datapoints)
|
||
{
|
||
datapoints->log("nbits_spec", &nbits_spec, sizeof(nbits_spec) );
|
||
datapoints->log("bytes_ari", &bytes[0], sizeof(uint8_t)*nbytes);
|
||
}
|
||
|
||
}
|
||
|
||
void EncoderFrame::registerDatapoints(DatapointContainer* datapoints_)
|
||
{
|
||
datapoints = datapoints_;
|
||
if (nullptr != datapoints)
|
||
{
|
||
bandwidthDetector.registerDatapoints(datapoints);
|
||
longTermPostfilter.registerDatapoints(datapoints);
|
||
bitstreamEncoding.registerDatapoints(datapoints);
|
||
}
|
||
}
|
||
|
||
}//namespace Lc3Enc
|