/****************************************************************************** * * * Copyright (C) 2023 The Android Open Source Project * * 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. * ***************************************************************************** * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore */ #include #include "ixheaac_type_def.h" #include "iusace_bitbuffer.h" #include "iusace_cnst.h" #include "iusace_tns_usac.h" #include "iusace_psy_mod.h" #include "iusace_block_switch_const.h" #include "iusace_rom.h" static FLOAT32 iusace_lpc_eval_chebyshev_polyn(FLOAT32 x, FLOAT32 *coefs, WORD32 order) { WORD32 i; FLOAT32 b0, b1, b2, x2; x2 = 2.0f * x; b2 = 1.0f; b1 = x2 + coefs[1]; for (i = 2; i < order; i++) { b0 = x2 * b1 - b2 + coefs[i]; b2 = b1; b1 = b0; } return (x * b1 - b2 + 0.5f * coefs[order]); } VOID iusace_lpc_2_lsp_conversion(FLOAT32 *lpc, FLOAT32 *lsp, FLOAT32 *prev_lsp) { FLOAT32 sum_polyn[(ORDER_BY_2) + 1], diff_polyn[(ORDER_BY_2) + 1]; FLOAT32 *p1_lpc, *p2_lpc, *p_sum_polyn, *p_diff_polyn; WORD32 i, j = 0, num_found_freeq = 0, is_first_polyn = 0; FLOAT32 x_low, y_low, x_high, y_high, x_mid, y_mid, x_lin_interp; p_sum_polyn = sum_polyn; p_diff_polyn = diff_polyn; *p_sum_polyn++ = 1.0f; *p_diff_polyn++ = 1.0f; sum_polyn[0] = 1.0f; diff_polyn[0] = 1.0f; p1_lpc = lpc + 1; p2_lpc = lpc + ORDER; for (i = 0; i <= ORDER_BY_2 - 1; i++) { *p_sum_polyn = *p1_lpc + *p2_lpc - *(p_sum_polyn - 1); p_sum_polyn++; *p_diff_polyn = *p1_lpc++ - *p2_lpc-- + *(p_diff_polyn - 1); p_diff_polyn++; } p_sum_polyn = sum_polyn; x_low = iusace_chebyshev_polyn_grid[0]; y_low = iusace_lpc_eval_chebyshev_polyn(x_low, p_sum_polyn, ORDER_BY_2); while ((num_found_freeq < ORDER) && (j < CHEBYSHEV_NUM_POINTS)) { j++; x_high = x_low; y_high = y_low; x_low = iusace_chebyshev_polyn_grid[j]; y_low = iusace_lpc_eval_chebyshev_polyn(x_low, p_sum_polyn, ORDER_BY_2); if (y_low * y_high <= 0.0) /* if sign change new root exists */ { j--; for (i = 0; i < CHEBYSHEV_NUM_ITER; i++) { x_mid = 0.5f * (x_low + x_high); y_mid = iusace_lpc_eval_chebyshev_polyn(x_mid, p_sum_polyn, ORDER_BY_2); if (y_low * y_mid <= 0.0) { y_high = y_mid; x_high = x_mid; } else { y_low = y_mid; x_low = x_mid; } } /* linear interpolation for evaluating the root */ x_lin_interp = x_low - y_low * (x_high - x_low) / (y_high - y_low); lsp[num_found_freeq] = x_lin_interp; num_found_freeq++; is_first_polyn = 1 - is_first_polyn; p_sum_polyn = is_first_polyn ? diff_polyn : sum_polyn; x_low = x_lin_interp; y_low = iusace_lpc_eval_chebyshev_polyn(x_low, p_sum_polyn, ORDER_BY_2); } } /* Check if ORDER roots found */ /* if not use the LSPs from previous frame */ if (num_found_freeq < ORDER) { for (i = 0; i < ORDER; i++) lsp[i] = prev_lsp[i]; } } static VOID iusace_compute_coeff_poly_f(FLOAT32 *lsp, FLOAT32 *poly1, FLOAT32 *poly2) { FLOAT32 b1, b2; FLOAT32 *ptr_lsp; WORD32 i, j; ptr_lsp = lsp; poly1[0] = poly2[0] = 1.0f; for (i = 1; i <= ORDER_BY_2; i++) { b1 = -2.0f * (*ptr_lsp++); b2 = -2.0f * (*ptr_lsp++); poly1[i] = (b1 * poly1[i - 1]) + (2.0f * poly1[i - 2]); poly2[i] = (b2 * poly2[i - 1]) + (2.0f * poly2[i - 2]); for (j = i - 1; j > 0; j--) { poly1[j] += (b1 * poly1[j - 1]) + poly1[j - 2]; poly2[j] += (b2 * poly2[j - 1]) + poly2[j - 2]; } } } VOID iusace_lsp_to_lp_conversion(FLOAT32 *lsp, FLOAT32 *lp_flt_coff_a) { WORD32 i; FLOAT32 *ppoly_f1, *ppoly_f2; FLOAT32 *plp_flt_coff_a_bott, *plp_flt_coff_a_top; FLOAT32 poly1[ORDER_BY_2 + 2], poly2[ORDER_BY_2 + 2]; poly1[0] = 0.0f; poly2[0] = 0.0f; iusace_compute_coeff_poly_f(lsp, &poly1[1], &poly2[1]); ppoly_f1 = poly1 + ORDER_BY_2 + 1; ppoly_f2 = poly2 + ORDER_BY_2 + 1; for (i = 0; i < ORDER_BY_2; i++) { ppoly_f1[0] += ppoly_f1[-1]; ppoly_f2[0] -= ppoly_f2[-1]; ppoly_f1--; ppoly_f2--; } plp_flt_coff_a_bott = lp_flt_coff_a; *plp_flt_coff_a_bott++ = 1.0f; plp_flt_coff_a_top = lp_flt_coff_a + ORDER; ppoly_f1 = poly1 + 2; ppoly_f2 = poly2 + 2; for (i = 0; i < ORDER_BY_2; i++) { *plp_flt_coff_a_bott++ = 0.5f * (*ppoly_f1 + *ppoly_f2); *plp_flt_coff_a_top-- = 0.5f * (*ppoly_f1++ - *ppoly_f2++); } } VOID iusace_levinson_durbin_algo(FLOAT32 *auto_corr_input, FLOAT32 *lpc) { WORD32 i, j; FLOAT32 lpc_val, sum, sigma; FLOAT32 reflection_coeffs[LEV_DUR_MAX_ORDER]; lpc[0] = 1.0f; reflection_coeffs[0] = -auto_corr_input[1] / auto_corr_input[0]; lpc[1] = reflection_coeffs[0]; sigma = auto_corr_input[0] + auto_corr_input[1] * reflection_coeffs[0]; for (i = 2; i <= ORDER; i++) { sum = 0.0f; for (j = 0; j < i; j++) sum += auto_corr_input[i - j] * lpc[j]; reflection_coeffs[i - 1] = -sum / sigma; sigma = sigma * (1.0f - reflection_coeffs[i - 1] * reflection_coeffs[i - 1]); if (sigma <= 1.0E-09f) { for (j = i; j <= ORDER; j++) { reflection_coeffs[j - 1] = 0.0f; lpc[j] = 0.0f; } break; } for (j = 1; j <= (i / 2); j++) { lpc_val = lpc[j] + reflection_coeffs[i - 1] * lpc[i - j]; lpc[i - j] += reflection_coeffs[i - 1] * lpc[j]; lpc[j] = lpc_val; } lpc[i] = reflection_coeffs[i - 1]; } } VOID iusace_get_weighted_lpc(FLOAT32 *lpc, FLOAT32 *weighted_lpc) { WORD32 i; for (i = 0; i <= ORDER; i++) { weighted_lpc[i] = iusace_gamma_table[i] * lpc[i]; } } VOID iusace_lsp_2_lsf_conversion(FLOAT32 *lsp, FLOAT32 *lsf) { WORD32 i; for (i = 0; i < ORDER; i++) { lsf[i] = (FLOAT32)(acos(lsp[i]) * LSP_2_LSF_SCALE); } } VOID iusace_lsf_2_lsp_conversion(FLOAT32 *lsf, FLOAT32 *lsp) { WORD32 i; for (i = 0; i < ORDER; i++) lsp[i] = (FLOAT32)cos((FLOAT64)lsf[i] * (FLOAT64)PI_BY_6400); }