/* * TemporalNoiseShaping.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 "TemporalNoiseShaping.hpp" #include "TemporalNoiseShapingTables.hpp" #include namespace Lc3Enc { TemporalNoiseShaping::TemporalNoiseShaping(const Lc3Config& lc3Config_) : lc3Config(lc3Config_), nbits_TNS(0), X_f(nullptr), tns_lpc_weighting(0), num_tns_filters(0), datapoints(nullptr) { X_f = new double[lc3Config.NE]; } TemporalNoiseShaping::~TemporalNoiseShaping() { if (nullptr != X_f) { delete[] X_f; } } void TemporalNoiseShaping::run(const double* const X_s, uint8_t P_BW, uint16_t nbits) { // 3.3.8.1 Overview / Table 3.14: TNS encoder parameters (d09r06_FhG) uint16_t start_freq[2]; uint16_t stop_freq[2]; uint16_t sub_start[2][3]; uint16_t sub_stop[2][3]; if (lc3Config.N_ms==Lc3Config::FrameDuration::d10ms) { start_freq[0] = 12; if (4!=P_BW) { start_freq[1] = 160; } else { start_freq[1] = 200; } switch(P_BW) { default: // Note: this default setting avoids uninitialized variables, but in // practice the calling code has the responsiblity to // ensure proper values of P_BW case 0: num_tns_filters = 1; stop_freq[0] = 80; sub_start[0][0] = 12; sub_start[0][1] = 34; sub_start[0][2] = 57; sub_stop[0][0] = 34; sub_stop[0][1] = 57; sub_stop[0][2] = 80; break; case 1: num_tns_filters = 1; stop_freq[0] = 160; sub_start[0][0] = 12; sub_start[0][1] = 61; sub_start[0][2] = 110; sub_stop[0][0] = 61; sub_stop[0][1] = 110; sub_stop[0][2] = 160; break; case 2: num_tns_filters = 1; stop_freq[0] = 240; sub_start[0][0] = 12; sub_start[0][1] = 88; sub_start[0][2] = 164; sub_stop[0][0] = 88; sub_stop[0][1] = 164; sub_stop[0][2] = 240; break; case 3: num_tns_filters = 2; stop_freq[0] = 160; stop_freq[1] = 320; sub_start[0][0] = 12; sub_start[0][1] = 61; sub_start[0][2] = 110; sub_start[1][0] = 160; sub_start[1][1] = 213; sub_start[1][2] = 266; sub_stop[0][0] = 61; sub_stop[0][1] = 110; sub_stop[0][2] = 160; sub_stop[1][0] = 213; sub_stop[1][1] = 266; sub_stop[1][2] = 320; break; case 4: num_tns_filters = 2; stop_freq[0] = 200; stop_freq[1] = 400; sub_start[0][0] = 12; sub_start[0][1] = 74; sub_start[0][2] = 137; sub_start[1][0] = 200; sub_start[1][1] = 266; sub_start[1][2] = 333; sub_stop[0][0] = 74; sub_stop[0][1] = 137; sub_stop[0][2] = 200; sub_stop[1][0] = 266; sub_stop[1][1] = 333; sub_stop[1][2] = 400; break; } } else {//7.5ms frame duration start_freq[0] = 9; if (4!=P_BW) { start_freq[1] = 119; } else { start_freq[1] = 150; } switch(P_BW) { default: // Note: this default setting avoids uninitialized variables, but in // practice the calling code has the responsiblity to // ensure proper values of P_BW case 0: num_tns_filters = 1; stop_freq[0] = 60; sub_start[0][0] = 9; sub_start[0][1] = 26; sub_start[0][2] = 43; sub_stop[0][0] = 26; sub_stop[0][1] = 43; sub_stop[0][2] = 60; break; case 1: num_tns_filters = 1; stop_freq[0] = 120; sub_start[0][0] = 9; sub_start[0][1] = 46; sub_start[0][2] = 83; sub_stop[0][0] = 46; sub_stop[0][1] = 83; sub_stop[0][2] = 120; break; case 2: num_tns_filters = 1; stop_freq[0] = 180; sub_start[0][0] = 9; sub_start[0][1] = 66; sub_start[0][2] = 123; sub_stop[0][0] = 66; sub_stop[0][1] = 123; sub_stop[0][2] = 180; break; case 3: num_tns_filters = 2; stop_freq[0] = 119; stop_freq[1] = 240; sub_start[0][0] = 9; sub_start[0][1] = 46; sub_start[0][2] = 82; //sub_start[1][0] = 119; // this value is specified in Table 3.14 (d09r06_KLG_AY_NH_FhG, 2019-12-20), but is inconsistent 32kHz decoder(!) tests compared to reference decoder sub_start[1][0] = 120; // this value gives good match to reference decoder(!) and is more consistent to 10ms case sub_start[1][1] = 159; sub_start[1][2] = 200; sub_stop[0][0] = 46; sub_stop[0][1] = 82; //sub_stop[0][2] = 119; // this value is specified in Table 3.14 (d09r06_KLG_AY_NH_FhG, 2019-12-20), but is inconsistent 32kHz decoder(!) tests compared to reference decoder sub_stop[0][2] = 120; // this value gives good match to reference decoder(!) and is more consistent to 10ms case sub_stop[1][0] = 159; sub_stop[1][1] = 200; sub_stop[1][2] = 240; break; case 4: num_tns_filters = 2; stop_freq[0] = 150; stop_freq[1] = 300; sub_start[0][0] = 9; sub_start[0][1] = 56; sub_start[0][2] = 103; sub_start[1][0] = 150; sub_start[1][1] = 200; sub_start[1][2] = 250; sub_stop[0][0] = 56; sub_stop[0][1] = 103; sub_stop[0][2] = 150; sub_stop[1][0] = 200; sub_stop[1][1] = 250; sub_stop[1][2] = 300; break; } } // 3.3.8.2 TNS analysis (d09r02_F2F, d09r08) tns_lpc_weighting = (nbits < ((lc3Config.N_ms==Lc3Config::FrameDuration::d10ms) ? 480 : 360) ) ? 1 : 0; const double pi = std::acos(-1); const double expFactor = -0.5*(0.02*pi)*(0.02*pi); for (uint8_t f=0; f < num_tns_filters; f++) { double r[9]; double* rw=r; for (uint8_t k=0; k<=8; k++) { double r0 = (0==k) ? 3 : 0; double rk = 0; double eProd = 1; for (uint8_t s=0; s < 3; s++) { double es = 0; double ac = 0; for (int16_t n = sub_start[f][s]; n < sub_stop[f][s]; n++) { es += X_s[n]*X_s[n]; if (n < (sub_stop[f][s] - k) ) { ac += X_s[n]*X_s[n+k]; } } eProd *= es; rk += ac/es; } if (0==eProd) // is this floating-point comparision with 0 stable enough? { r[k] = r0; } else { r[k] = rk; } // lag-windowing (Note: in-place) rw[k] = r[k]*exp(expFactor*k*k); } // The Levinson-Durbin recursion is used to obtain LPC coefficients // and a predictor error double a_memory[2][9]; double* a = a_memory[0]; double* a_last = a_memory[1]; double e = rw[0]; a[0] = 1; for (uint8_t k=1; k <= 8; k++) { // swap buffer for a and a_last double* tmp = a_last; a_last = a; a = tmp; // a:=a^k; a_last:=a^(k-1) double rc = 0; for (uint8_t n=0; n < k; n++) { rc -= a_last[n] * rw[k-n]; } if (0==e) { // is this handling to avoid division by 0 ok? e = 1; } rc /= e; a[0] = 1; for (uint8_t n=1; n < k; n++) { a[n] = a_last[n] + rc*a_last[k-n]; } a[k] = rc; e = (1-rc*rc)*e; } double predGain = rw[0]/e; // again a suspicious possible division-by-zero! (should be handled above) const double thresh = 1.5; if ( predGain <= thresh ) { // turn TNS filter f off double* rc = &rc_q[f*8+0]; for (uint8_t n=0; n < 8; n++) { rc[n] = 0; } } else { // turn TNS filter f on // -> we need to compute reflection coefficients double gamma = 1; const double thresh2 = 2; if ( (tns_lpc_weighting>0) && (predGain < thresh2) ) { const double gamma_min = 0.85; gamma -= (1-gamma_min) * (thresh2-predGain) / (thresh2-thresh); } double* aw = a; // compute in-place for (uint8_t k=0; k < 9; k++) { aw[k] = pow(gamma, k)*a[k]; } double* rc = &rc_q[f*8+0]; double* a_k = aw; double* a_km1 = a_last; for (uint8_t k=8; k > 0; k--) { rc[k-1] = a_k[k]; double e = (1-rc[k-1]*rc[k-1]); for (uint8_t n=1; n < k; n++) { a_km1[n] = a_k[n] - rc[k-1]*a_k[k-n]; a_km1[n] /= e; } // swap buffer for a_k and a_km1 double* tmp = a_k; a_k = a_km1; a_km1 = tmp; } } } // 3.3.8.3 Quantization (d09r02_F2F) // with Δ =π/17 const double quantizer_stepsize = pi/17; for (uint8_t f=0; f < num_tns_filters; f++) { double* rc = &rc_q[f*8+0]; // source (see above) for (uint8_t k=0; k < 8; k++) { // attention: rc in place of rc_q! rc_i[f*8+k] = nint( asin(rc[k]) / quantizer_stepsize ) + 8; rc_q[f*8+k] = sin( quantizer_stepsize * (rc_i[f*8+k]-8) ); } // determine order of quantized reflection coefficients int8_t k=7; // need signed to stop while properly //while( (k>=0) && (0==rc_q[f*8+k]) ) // specification while( (k>=0) && (8==rc_i[f*8+k]) ) // alternative solution that should be more robust (and faster) { k--; } rc_order[f] = k+1; } for (uint8_t f=num_tns_filters; f < 2; f++) { for (uint8_t k=0; k < 8; k++) { rc_i[f*8+k] = 8; rc_q[f*8+k] = 0; } rc_order[f] = 0; } // bit budget nbits_TNS = 0; for (uint8_t f=0; f < num_tns_filters; f++) { uint32_t nbits_TNS_local = 0; uint32_t nbits_TNS_order = 0; if (rc_order[f] > 0) { nbits_TNS_order = ac_tns_order_bits[tns_lpc_weighting][rc_order[f]-1]; } uint32_t nbits_TNS_coef = 0; for (uint8_t k=0; k < rc_order[f]; k++) { nbits_TNS_coef += ac_tns_coef_bits[k][rc_i[f*8+k]]; } nbits_TNS_local += 2048 + nbits_TNS_order + nbits_TNS_coef; if (nullptr!=datapoints) { if (f==0) { datapoints->log("nbits_TNS_order_0", &nbits_TNS_order, sizeof(uint32_t)); datapoints->log("nbits_TNS_coef_0", &nbits_TNS_coef, sizeof(uint32_t)); } else { datapoints->log("nbits_TNS_order_1", &nbits_TNS_order, sizeof(uint32_t)); datapoints->log("nbits_TNS_coef_1", &nbits_TNS_coef, sizeof(uint32_t)); } } nbits_TNS += ceil( nbits_TNS_local / 2048.0 ); // error in specification ((d09r08) -> we need to do it this way to be consistent with reference application; spec. will be changed later } // 3.3.8.4 Filtering (d09r02_F2F) for (uint16_t k = 0; k < lc3Config.NE; k++) { X_f[k] = X_s[k]; } double st[8]; for (uint8_t k = 0; k<8; k++) { st[k]=0; } for (uint8_t f=0; f < num_tns_filters; f++) { if (rc_order[f]>0) { for (uint16_t n = start_freq[f]; n < stop_freq[f]; n++) { double t = X_s[n]; double st_save = t; for (uint8_t k=0; k < rc_order[f]-1; k++) { double st_tmp = rc_q[f*8+k] * t + st[k]; t += rc_q[f*8+k] * st[k]; st[k] = st_save; st_save = st_tmp; } t += rc_q[f*8 + rc_order[f]-1] * st[rc_order[f]-1]; st[rc_order[f]-1] = st_save; X_f[n] = t; } } } } int8_t TemporalNoiseShaping::nint(double x) { if (x >= 0) { return static_cast(x+0.5); } else { return -static_cast(-x+0.5); } } void TemporalNoiseShaping::registerDatapoints(DatapointContainer* datapoints_) { datapoints = datapoints_; if (nullptr != datapoints) { datapoints->addDatapoint( "X_f", &X_f[0], sizeof(double)*lc3Config.NE ); datapoints->addDatapoint( "num_tns_filters", &num_tns_filters, sizeof(num_tns_filters) ); datapoints->addDatapoint( "rc_order", &rc_order[0], sizeof(uint8_t)*2 ); datapoints->addDatapoint( "rc_i_1", &rc_i[0], sizeof(uint8_t)*8 ); datapoints->addDatapoint( "rc_i_2", &rc_i[8], sizeof(uint8_t)*8 ); datapoints->addDatapoint( "rc_q_1", &rc_q[0], sizeof(double)*8 ); datapoints->addDatapoint( "rc_q_2", &rc_q[8], sizeof(double)*8 ); datapoints->addDatapoint( "nbits_TNS", &nbits_TNS, sizeof(nbits_TNS) ); } } }//namespace Lc3Enc