1/******************************************************************************
2 *                                                                            *
3 * Copyright (C) 2018 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *****************************************************************************
18 * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
19*/
20#include <ixheaacd_type_def.h>
21#include "ixheaacd_bitbuffer.h"
22#include "ixheaacd_config.h"
23
24#include "ixheaacd_mps_polyphase.h"
25
26#include "ixheaacd_mps_dec.h"
27#include "ixheaacd_mps_interface.h"
28#include <math.h>
29
30#define max(a, b) ((a) > (b) ? (a) : (b))
31
32#define min(a, b) ((a) < (b) ? (a) : (b))
33
34#define DIR_DIFF_IN 0
35#define DOWNMIX_IN 1
36
37#define LAMDA (4.0f)
38#define GES_ALPHA (0.99637864f)
39#define GES_BETA (0.9643691f)
40
41extern WORD32
42    ixheaacd_hybrid_band_71_to_processing_band_20_map[MAX_HYBRID_BANDS_MPS];
43
44VOID ixheaacd_mps_env_init(ia_mps_dec_state_struct *self) {
45  WORD32 i;
46  for (i = 0; i < 3; i++) {
47    self->guided_env_shaping.avg_energy_prev[i] = 32768.f * 32768.f;
48  }
49}
50
51static VOID ixheaacd_mps_est_normalized_envelope(ia_mps_dec_state_struct *self,
52                                                 WORD32 inp, WORD32 ch,
53                                                 FLOAT32 *env) {
54  FLOAT32 slot_energy[MAX_TIME_SLOTS][MAX_PARAMETER_BANDS] = {{0}};
55  FLOAT32 pb_energy[MAX_PARAMETER_BANDS] = {0};
56  FLOAT32 whitening_weight[MAX_PARAMETER_BANDS];
57  WORD32 ii, jj, param_band;
58
59  WORD32 k_start = 10;
60  WORD32 k_stop = 18;
61
62  FLOAT32 total_energy = 0, avg_energy = 0;
63
64  WORD32 ch_offset;
65
66  switch (inp) {
67    case DIR_DIFF_IN:
68      ch_offset = 0;
69      for (ii = 0; ii < self->time_slots; ii++) {
70        for (jj = 0; jj < self->hyb_band_count; jj++) {
71          slot_energy[ii]
72                     [ixheaacd_hybrid_band_71_to_processing_band_20_map[jj]] +=
73              ((self->hyb_dir_out[ch][ii][jj].re +
74                self->hyb_diff_out[ch][ii][jj].re) *
75               (self->hyb_dir_out[ch][ii][jj].re +
76                self->hyb_diff_out[ch][ii][jj].re)) +
77              ((self->hyb_dir_out[ch][ii][jj].im +
78                self->hyb_diff_out[ch][ii][jj].im) *
79               (self->hyb_dir_out[ch][ii][jj].im +
80                self->hyb_diff_out[ch][ii][jj].im));
81        }
82      }
83      break;
84    case DOWNMIX_IN:
85      ch_offset = self->out_ch_count;
86      for (ii = 0; ii < self->time_slots; ii++) {
87        for (jj = 0; jj < self->hyb_band_count; jj++) {
88          slot_energy[ii]
89                     [ixheaacd_hybrid_band_71_to_processing_band_20_map[jj]] +=
90              self->hyb_in[ch][ii][jj].re * self->hyb_in[ch][ii][jj].re +
91              self->hyb_in[ch][ii][jj].im * self->hyb_in[ch][ii][jj].im;
92        }
93      }
94      break;
95    default:
96      ch_offset = 0;
97      break;
98  }
99
100  for (param_band = k_start; param_band <= k_stop; param_band++)
101    pb_energy[param_band] =
102        self->guided_env_shaping.pb_energy_prev[ch + ch_offset][param_band];
103
104  avg_energy = self->guided_env_shaping.avg_energy_prev[ch + ch_offset];
105
106  for (ii = 0; ii < self->time_slots; ii++) {
107    total_energy = 0;
108    for (param_band = k_start; param_band <= k_stop; param_band++) {
109      pb_energy[param_band] = (1 - GES_ALPHA) * slot_energy[ii][param_band] +
110                              GES_ALPHA * pb_energy[param_band];
111
112      total_energy += slot_energy[ii][param_band];
113    }
114    total_energy /= (k_stop - k_start + 1);
115
116    total_energy =
117        (1 - GES_ALPHA) * total_energy +
118        GES_ALPHA * self->guided_env_shaping.frame_energy_prev[ch + ch_offset];
119
120    self->guided_env_shaping.frame_energy_prev[ch + ch_offset] = total_energy;
121
122    for (param_band = k_start; param_band <= k_stop; param_band++) {
123      whitening_weight[param_band] =
124          total_energy / (pb_energy[param_band] + ABS_THR);
125    }
126
127    env[ii] = 0;
128    for (param_band = k_start; param_band <= k_stop; param_band++) {
129      env[ii] += slot_energy[ii][param_band] * whitening_weight[param_band];
130    }
131
132    avg_energy = (1 - GES_BETA) * env[ii] + GES_BETA * avg_energy;
133
134    env[ii] = (FLOAT32)sqrt(env[ii] / (avg_energy + ABS_THR));
135  }
136
137  for (param_band = k_start; param_band <= k_stop; param_band++)
138    self->guided_env_shaping.pb_energy_prev[ch + ch_offset][param_band] =
139        pb_energy[param_band];
140
141  self->guided_env_shaping.avg_energy_prev[ch + ch_offset] = avg_energy;
142}
143
144VOID ixheaacd_mps_time_env_shaping(ia_mps_dec_state_struct *self) {
145  FLOAT32 dir_energy[MAX_TIME_SLOTS];
146  FLOAT32 dmx_energy[MAX_TIME_SLOTS];
147  WORD32 ch, time_slot, jj;
148
149  WORD32 band_start;
150  FLOAT32 gain, ratio;
151
152  FLOAT32 amp_direct = 0;
153  FLOAT32 amp_diff = 0;
154  FLOAT32 amp_ratio;
155
156  band_start = 6;
157
158  ixheaacd_mps_est_normalized_envelope(self, DOWNMIX_IN, 0, dmx_energy);
159
160  for (ch = 0; ch < self->out_ch_count; ch++) {
161    ixheaacd_mps_est_normalized_envelope(self, DIR_DIFF_IN, ch, dir_energy);
162
163    if (self->temp_shape_enable_ch_ges[ch]) {
164      for (time_slot = 0; time_slot < self->time_slots; time_slot++) {
165        gain = self->env_shape_data[ch][time_slot] * dmx_energy[time_slot] /
166               (dir_energy[time_slot] + 1e-9f);
167
168        amp_direct = 0;
169        amp_diff = 0;
170
171        for (jj = band_start; jj < self->hyb_band_count; jj++) {
172          amp_direct += self->hyb_dir_out[ch][time_slot][jj].re *
173                            self->hyb_dir_out[ch][time_slot][jj].re +
174                        self->hyb_dir_out[ch][time_slot][jj].im *
175                            self->hyb_dir_out[ch][time_slot][jj].im;
176
177          amp_diff += self->hyb_diff_out[ch][time_slot][jj].re *
178                          self->hyb_diff_out[ch][time_slot][jj].re +
179                      self->hyb_diff_out[ch][time_slot][jj].im *
180                          self->hyb_diff_out[ch][time_slot][jj].im;
181        }
182
183        amp_ratio = (FLOAT32)sqrt(amp_diff / (amp_direct + ABS_THR));
184
185        ratio = min(max((gain + amp_ratio * (gain - 1)), 1 / LAMDA), LAMDA);
186
187        for (jj = band_start; jj < self->hyb_band_count; jj++) {
188          self->hyb_dir_out[ch][time_slot][jj].re *= ratio;
189          self->hyb_dir_out[ch][time_slot][jj].im *= ratio;
190        }
191      }
192    }
193  }
194}
195