aboutsummaryrefslogtreecommitdiff
path: root/decoder/ixheaacd_peak_limiter.c
diff options
context:
space:
mode:
Diffstat (limited to 'decoder/ixheaacd_peak_limiter.c')
-rw-r--r--decoder/ixheaacd_peak_limiter.c206
1 files changed, 206 insertions, 0 deletions
diff --git a/decoder/ixheaacd_peak_limiter.c b/decoder/ixheaacd_peak_limiter.c
new file mode 100644
index 0000000..9c99e94
--- /dev/null
+++ b/decoder/ixheaacd_peak_limiter.c
@@ -0,0 +1,206 @@
+/******************************************************************************
+ * *
+ * 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 <stdlib.h>
+#include <math.h>
+#include <ixheaacd_type_def.h>
+#include "ixheaacd_cnst.h"
+#include "ixheaacd_peak_limiter_struct_def.h"
+#include "ixheaacd_constants.h"
+#include "ixheaacd_basic_ops32.h"
+#include "ixheaacd_basic_ops16.h"
+
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#define MIN(x, y) ((x) > (y) ? (y) : (x))
+
+/**
+* ixheaacd_peak_limiter_init
+*
+* \brief Peak Limiter initialization
+*
+* \param [in/out] peak_limiter Pointer to peak_limiter struct
+* \param [in] num_channels Number of ouptut channels
+* \param [in] sample_rate Sampling rate value
+* \param [in] buffer Peak limiter buffer of size PEAK_LIM_BUFFER_SIZE
+*
+* \return WORD32
+*
+*/
+WORD32 ixheaacd_peak_limiter_init(ia_peak_limiter_struct *peak_limiter,
+ UWORD32 num_channels, UWORD32 sample_rate,
+ FLOAT32 *buffer, UWORD32 *delay_in_samples) {
+ UWORD32 attack;
+
+ attack = (UWORD32)(DEFAULT_ATTACK_TIME_MS * sample_rate / 1000);
+ *delay_in_samples = attack;
+
+ if (attack < 1) return 0;
+
+ peak_limiter->max_buf = buffer;
+ peak_limiter->max_idx = 0;
+ peak_limiter->cir_buf_pnt = 0;
+ peak_limiter->delayed_input = buffer + attack + 1;
+
+ peak_limiter->delayed_input_index = 0;
+ peak_limiter->attack_time = DEFAULT_ATTACK_TIME_MS;
+ peak_limiter->release_time = DEFAULT_RELEASE_TIME_MS;
+ peak_limiter->attack_time_samples = attack;
+ peak_limiter->attack_constant = (FLOAT32)pow(0.1, 1.0 / (attack + 1));
+ peak_limiter->release_constant = (FLOAT32)pow(
+ 0.1, 1.0 / (DEFAULT_RELEASE_TIME_MS * sample_rate / 1000 + 1));
+ peak_limiter->num_channels = num_channels;
+ peak_limiter->sample_rate = sample_rate;
+ peak_limiter->min_gain = 1.0f;
+ peak_limiter->limiter_on = 1;
+ peak_limiter->pre_smoothed_gain = 1.0f;
+ peak_limiter->gain_modified = 1.0f;
+
+ return 0;
+}
+
+/**
+* ixheaacd_peak_limiter_process
+*
+* \brief Peak Limiter process
+*
+* \param [in/out] peak_limiter
+* \param [in] samples
+* \param [in] frame_len
+*
+* \return WORD32
+*
+*/
+VOID ixheaacd_peak_limiter_process(ia_peak_limiter_struct *peak_limiter,
+ VOID *samples_t, UWORD32 frame_len,
+ UWORD8 *qshift_adj) {
+ UWORD32 i, j;
+ FLOAT32 tmp, gain;
+ FLOAT32 min_gain = 1.0f;
+ FLOAT32 maximum;
+ UWORD32 num_channels = peak_limiter->num_channels;
+ UWORD32 attack_time_samples = peak_limiter->attack_time_samples;
+ FLOAT32 attack_constant = peak_limiter->attack_constant;
+ FLOAT32 release_constant = peak_limiter->release_constant;
+ FLOAT32 *max_buf = peak_limiter->max_buf;
+ FLOAT32 gain_modified = peak_limiter->gain_modified;
+ FLOAT32 *delayed_input = peak_limiter->delayed_input;
+ UWORD32 delayed_input_index = peak_limiter->delayed_input_index;
+ FLOAT64 pre_smoothed_gain = peak_limiter->pre_smoothed_gain;
+ WORD32 limit_threshold = 2147483647;
+
+ WORD32 *samples = (WORD32 *)samples_t;
+
+ if (peak_limiter->limiter_on || (FLOAT32)pre_smoothed_gain) {
+ for (i = 0; i < frame_len; i++) {
+ tmp = 0.0f;
+ for (j = 0; j < num_channels; j++) {
+ FLOAT32 gain_t = (FLOAT32)(1 << *(qshift_adj + j));
+ tmp = MAX(tmp, fabs((samples[i * num_channels + j] * gain_t)));
+ }
+ max_buf[peak_limiter->cir_buf_pnt] = tmp;
+
+ if (peak_limiter->max_idx == peak_limiter->cir_buf_pnt) {
+ peak_limiter->max_idx = 0;
+ for (j = 1; j < (attack_time_samples); j++) {
+ if (max_buf[j] > max_buf[peak_limiter->max_idx])
+ peak_limiter->max_idx = j;
+ }
+ } else if (tmp >= max_buf[peak_limiter->max_idx]) {
+ peak_limiter->max_idx = peak_limiter->cir_buf_pnt;
+ }
+ peak_limiter->cir_buf_pnt++;
+
+ if (peak_limiter->cir_buf_pnt == (WORD32)(attack_time_samples))
+ peak_limiter->cir_buf_pnt = 0;
+ maximum = max_buf[peak_limiter->max_idx];
+
+ if (maximum > limit_threshold) {
+ gain = limit_threshold / maximum;
+ } else {
+ gain = 1;
+ }
+
+ if (gain < pre_smoothed_gain) {
+ gain_modified =
+ MIN(gain_modified,
+ (gain - 0.1f * (FLOAT32)pre_smoothed_gain) * 1.11111111f);
+
+ } else {
+ gain_modified = gain;
+ }
+
+ if (gain_modified < pre_smoothed_gain) {
+ pre_smoothed_gain =
+ attack_constant * (pre_smoothed_gain - gain_modified) +
+ gain_modified;
+ pre_smoothed_gain = MAX(pre_smoothed_gain, gain);
+ } else {
+ pre_smoothed_gain =
+ release_constant * (pre_smoothed_gain - gain_modified) +
+ gain_modified;
+ }
+
+ gain = (FLOAT32)pre_smoothed_gain;
+
+ for (j = 0; j < num_channels; j++) {
+ WORD64 tmp_fix;
+ tmp = delayed_input[delayed_input_index * num_channels + j];
+ FLOAT32 gain_t = (FLOAT32)(1 << *(qshift_adj + j));
+ delayed_input[delayed_input_index * num_channels + j] =
+ samples[i * num_channels + j] * gain_t;
+
+ tmp *= gain;
+
+ tmp_fix = tmp;
+
+ if (tmp_fix > limit_threshold)
+ tmp_fix = limit_threshold;
+ else if (tmp_fix < -limit_threshold)
+ tmp_fix = -limit_threshold;
+
+ samples[i * num_channels + j] = tmp_fix;
+ }
+
+ delayed_input_index++;
+ if (delayed_input_index >= attack_time_samples) delayed_input_index = 0;
+
+ if (gain < min_gain) min_gain = gain;
+ }
+ } else {
+ for (i = 0; i < frame_len; i++) {
+ for (j = 0; j < num_channels; j++) {
+ tmp = delayed_input[delayed_input_index * num_channels + j];
+ FLOAT32 gain_t = (FLOAT32)(1 << *(qshift_adj + j));
+ delayed_input[delayed_input_index * num_channels + j] =
+ samples[i * num_channels + j] * gain_t;
+ samples[i * num_channels + j] = tmp;
+ }
+
+ delayed_input_index++;
+ if (delayed_input_index >= attack_time_samples) delayed_input_index = 0;
+ }
+ }
+
+ peak_limiter->gain_modified = gain_modified;
+ peak_limiter->delayed_input_index = delayed_input_index;
+ peak_limiter->pre_smoothed_gain = pre_smoothed_gain;
+ peak_limiter->min_gain = min_gain;
+
+ return;
+}