| /****************************************************************************** |
| * * |
| * Copyright (C) 2018 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 <stdio.h> |
| #include <stdlib.h> |
| #include <math.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| #include "ixheaacd_type_def.h" |
| #include "ixheaacd_bitbuffer.h" |
| #include "ixheaacd_config.h" |
| |
| #include "ixheaacd_mps_polyphase.h" |
| #include "ixheaacd_mps_dec.h" |
| #include "ixheaacd_mps_interface.h" |
| |
| #include "ixheaacd_mps_polyphase.h" |
| |
| #include "ixheaacd_mps_decor.h" |
| #include "ixheaacd_mps_hybfilter.h" |
| #include "ixheaacd_error_standards.h" |
| #include "ixheaacd_constants.h" |
| |
| static const WORD32 ixheaacd_decorr_delay[] = {11, 10, 5, 2}; |
| |
| static const WORD32 ixheaacd_qmf_split_freq_0[] = {3, 15, 24, 65}; |
| static const WORD32 ixheaacd_qmf_split_freq_1[] = {3, 50, 65, 65}; |
| static const WORD32 ixheaacd_qmf_split_freq_2[] = {0, 15, 65, 65}; |
| |
| static const FLOAT32 |
| ixheaacd_lattice_coeff_0_filt_den_coeff[DECORR_FILT_0_ORD + 1] = { |
| 1.000000f, -0.314818f, -0.256828f, -0.173641f, -0.115077f, 0.000599f, |
| 0.033343f, 0.122672f, -0.356362f, 0.128058f, 0.089800f}; |
| static const FLOAT32 |
| ixheaacd_lattice_coeff_0_filt_num_coeff[DECORR_FILT_0_ORD + 1] = { |
| 0.089800f, 0.128058f, -0.356362f, 0.122672f, 0.033343f, 0.000599f, |
| -0.115077f, -0.173641f, -0.256828f, -0.314818f, 1.000000f}; |
| |
| static const FLOAT32 |
| ixheaacd_lattice_coeff_1_filt_den_coeff[DECORR_FILT_1_ORD + 1] = { |
| 1.000000f, -0.287137f, -0.088940f, 0.123204f, -0.126111f, |
| 0.064218f, 0.045768f, -0.016264f, -0.122100f}; |
| static const FLOAT32 |
| ixheaacd_lattice_coeff_1_filt_num_coeff[DECORR_FILT_1_ORD + 1] = { |
| -0.122100f, -0.016264f, 0.045768f, 0.064218f, -0.126111f, |
| 0.123204f, -0.088940f, -0.287137f, 1.000000f}; |
| |
| static const FLOAT32 |
| ixheaacd_lattice_coeff_2_filt_den_coeff[DECORR_FILT_2_ORD + 1] = { |
| 1.000000f, 0.129403f, -0.032633f, 0.035700f}; |
| static const FLOAT32 |
| ixheaacd_lattice_coeff_2_filt_num_coeff[DECORR_FILT_2_ORD + 1] = { |
| 0.035700f, -0.032633f, 0.129403f, 1.000000f}; |
| |
| static const FLOAT32 |
| ixheaacd_lattice_coeff_3_filt_den_coeff[DECORR_FILT_3_ORD + 1] = { |
| 1.000000f, 0.034742f, -0.013000f}; |
| static const FLOAT32 |
| ixheaacd_lattice_coeff_3_filt_num_coeff[DECORR_FILT_3_ORD + 1] = { |
| -0.013000f, 0.034742f, 1.000000f}; |
| |
| extern const WORD32 |
| ixheaacd_hybrid_band_71_to_processing_band_28_map[MAX_HYBRID_BANDS_MPS]; |
| |
| static const WORD32 ixheaacd_hybrid_to_qmf_map[MAX_HYBRID_BANDS_MPS] = { |
| 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
| 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, |
| 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, |
| 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; |
| |
| static void ixheaacd_mps_decor_filt_init(ia_mps_decor_filt_struct *self, |
| WORD32 reverb_band) { |
| switch (reverb_band) { |
| case 0: |
| self->num_len = self->den_len = DECORR_FILT_0_ORD + 1; |
| self->num = ixheaacd_lattice_coeff_0_filt_num_coeff; |
| self->den = ixheaacd_lattice_coeff_0_filt_den_coeff; |
| |
| break; |
| case 1: |
| self->num_len = self->den_len = DECORR_FILT_1_ORD + 1; |
| self->num = ixheaacd_lattice_coeff_1_filt_num_coeff; |
| self->den = ixheaacd_lattice_coeff_1_filt_den_coeff; |
| |
| break; |
| case 2: |
| self->num_len = self->den_len = DECORR_FILT_2_ORD + 1; |
| self->num = ixheaacd_lattice_coeff_2_filt_num_coeff; |
| self->den = ixheaacd_lattice_coeff_2_filt_den_coeff; |
| break; |
| case 3: |
| self->num_len = self->den_len = DECORR_FILT_3_ORD + 1; |
| self->num = ixheaacd_lattice_coeff_3_filt_num_coeff; |
| self->den = ixheaacd_lattice_coeff_3_filt_den_coeff; |
| break; |
| } |
| |
| self->state_len = self->num_len; |
| memset(self->state, 0, |
| sizeof(ia_cmplx_flt_struct) * (MAX_DECORR_FIL_ORDER + 1)); |
| |
| return; |
| } |
| |
| static VOID ixheaacd_mps_allpass_apply(ia_mps_decor_filt_struct *self, |
| ia_cmplx_flt_struct *input, WORD32 len, |
| ia_cmplx_flt_struct *output) { |
| WORD32 i, j; |
| |
| for (i = 0; i < len; i++) { |
| output[i].re = self->state[0].re + input[i].re * self->num[0]; |
| output[i].im = self->state[0].im + input[i].im * self->num[0]; |
| |
| for (j = 1; j < self->num_len; j++) { |
| self->state[j - 1].re = self->state[j].re + self->num[j] * input[i].re - |
| self->den[j] * output[i].re; |
| self->state[j - 1].im = self->state[j].im + self->num[j] * input[i].im - |
| self->den[j] * output[i].im; |
| } |
| } |
| } |
| |
| static VOID ixheaacd_mps_decor_energy_adjustment( |
| ixheaacd_mps_decor_energy_adjust_filt_struct *handle, |
| ia_cmplx_flt_struct in[MAX_TIME_SLOTS][MAX_HYBRID_BANDS_MPS], |
| ia_cmplx_flt_struct out[MAX_TIME_SLOTS][MAX_HYBRID_BANDS_MPS], |
| WORD32 time_slots) { |
| ixheaacd_mps_decor_energy_adjust_filt_struct *self = |
| (ixheaacd_mps_decor_energy_adjust_filt_struct *)handle; |
| FLOAT32 in_energy[MAX_PARAMETER_BANDS] = {0}; |
| FLOAT32 out_energy[MAX_PARAMETER_BANDS] = {0}; |
| FLOAT32 gain[MAX_PARAMETER_BANDS]; |
| WORD32 i, j, k; |
| |
| for (i = 0; i < time_slots; i++) { |
| memset(in_energy, 0, sizeof(FLOAT32) * MAX_PARAMETER_BANDS); |
| memset(out_energy, 0, sizeof(FLOAT32) * MAX_PARAMETER_BANDS); |
| |
| for (j = 0; j < self->num_bins; j++) { |
| k = ixheaacd_hybrid_band_71_to_processing_band_28_map[j]; |
| |
| in_energy[k] += in[i][j].re * in[i][j].re + in[i][j].im * in[i][j].im; |
| out_energy[k] += |
| out[i][j].re * out[i][j].re + out[i][j].im * out[i][j].im; |
| } |
| |
| for (k = 0; k < MAX_PARAMETER_BANDS; k++) { |
| self->smooth_in_energy[k] = self->smooth_in_energy[k] * DECOR_ALPHA + |
| in_energy[k] * ONE_MINUS_DECOR_ALPHA; |
| self->smooth_out_energy[k] = self->smooth_out_energy[k] * DECOR_ALPHA + |
| out_energy[k] * ONE_MINUS_DECOR_ALPHA; |
| |
| gain[k] = 1.0f; |
| |
| if (self->smooth_out_energy[k] > |
| self->smooth_in_energy[k] * DECOR_GAMMA) { |
| gain[k] = (FLOAT32)sqrt(self->smooth_in_energy[k] * DECOR_GAMMA / |
| (self->smooth_out_energy[k] + ABS_THR)); |
| } |
| |
| if (self->smooth_in_energy[k] > |
| self->smooth_out_energy[k] * DECOR_GAMMA) { |
| gain[k] = |
| min(2.0f, (FLOAT32)sqrt(self->smooth_in_energy[k] / |
| (DECOR_GAMMA * self->smooth_out_energy[k] + |
| ABS_THR))); |
| } |
| } |
| |
| for (j = 0; j < self->num_bins; j++) { |
| k = ixheaacd_hybrid_band_71_to_processing_band_28_map[j]; |
| |
| out[i][j].re *= gain[k]; |
| out[i][j].im *= gain[k]; |
| } |
| } |
| } |
| |
| IA_ERRORCODE ixheaacd_mps_decor_init(ia_mps_decor_struct_handle self, |
| WORD32 subbands, WORD32 decor_config) { |
| WORD32 i, reverb_band; |
| const WORD32 *splitfreq; |
| |
| switch (decor_config) { |
| case 0: |
| splitfreq = ixheaacd_qmf_split_freq_0; |
| break; |
| case 1: |
| splitfreq = ixheaacd_qmf_split_freq_1; |
| break; |
| case 2: |
| splitfreq = ixheaacd_qmf_split_freq_2; |
| break; |
| default: |
| return IA_FATAL_ERROR; |
| } |
| |
| self->num_bins = subbands; |
| |
| for (i = 0; i < self->num_bins; i++) { |
| reverb_band = 0; |
| while ((reverb_band < 3) && |
| (ixheaacd_hybrid_to_qmf_map[i] >= (splitfreq[reverb_band] - 1))) |
| reverb_band++; |
| |
| self->delay_sample_count[i] = ixheaacd_decorr_delay[reverb_band]; |
| ixheaacd_mps_decor_filt_init(&self->filter[i], reverb_band); |
| } |
| |
| self->decor_nrg_smooth.num_bins = self->num_bins; |
| |
| return IA_NO_ERROR; |
| } |
| |
| VOID ixheaacd_mps_decor_apply( |
| ia_mps_decor_struct_handle self, |
| ia_cmplx_flt_struct in[MAX_TIME_SLOTS][MAX_HYBRID_BANDS_MPS], |
| ia_cmplx_flt_struct out[MAX_TIME_SLOTS][MAX_HYBRID_BANDS_MPS], |
| WORD32 length) { |
| WORD32 idx, sb_sample; |
| |
| ia_cmplx_flt_struct scratch[MAX_TIME_SLOTS]; |
| |
| for (idx = 0; idx < self->num_bins; idx++) { |
| for (sb_sample = 0; sb_sample < length; sb_sample++) { |
| self->decor_delay_buffer[idx][self->delay_sample_count[idx] + sb_sample] |
| .re = in[sb_sample][idx].re; |
| self->decor_delay_buffer[idx][self->delay_sample_count[idx] + sb_sample] |
| .im = in[sb_sample][idx].im; |
| } |
| ixheaacd_mps_allpass_apply(&self->filter[idx], |
| self->decor_delay_buffer[idx], length, scratch); |
| |
| for (sb_sample = 0; sb_sample < length; sb_sample++) { |
| out[sb_sample][idx].re = scratch[sb_sample].re; |
| out[sb_sample][idx].im = scratch[sb_sample].im; |
| } |
| |
| for (sb_sample = 0; sb_sample < self->delay_sample_count[idx]; |
| sb_sample++) { |
| self->decor_delay_buffer[idx][sb_sample].re = |
| self->decor_delay_buffer[idx][length + sb_sample].re; |
| self->decor_delay_buffer[idx][sb_sample].im = |
| self->decor_delay_buffer[idx][length + sb_sample].im; |
| } |
| } |
| |
| ixheaacd_mps_decor_energy_adjustment(&self->decor_nrg_smooth, in, out, |
| length); |
| } |