blob: bbd6fd8964ed87fa70894d87a365db7af2f4e847 [file] [log] [blame]
/******************************************************************************
* *
* 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 "ixheaac_type_def.h"
#include "ixheaacd_cnst.h"
#include "ixheaacd_peak_limiter_struct_def.h"
#include "ixheaac_constants.h"
#include "ixheaac_basic_ops32.h"
#include "ixheaac_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 * 4 + 32;
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;
}
VOID ixheaacd_peak_limiter_process_float(ia_peak_limiter_struct *peak_limiter,
FLOAT32 samples[MAX_NUM_CHANNELS][4096],
UWORD32 frame_len) {
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;
FLOAT32 limit_threshold = PEAK_LIM_THR_FLOAT;
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++) {
tmp = (FLOAT32)MAX(tmp, fabs(samples[j][i]));
}
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++) {
tmp = delayed_input[delayed_input_index * num_channels + j];
delayed_input[delayed_input_index * num_channels + j] = samples[j][i];
tmp *= gain;
if (tmp > limit_threshold)
tmp = limit_threshold;
else if (tmp < -limit_threshold)
tmp = -limit_threshold;
samples[j][i] = tmp;
}
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];
delayed_input[delayed_input_index * num_channels + j] = samples[j][i];
samples[j][i] = 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;
}
/**
* 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 = PEAK_LIM_THR_FIX;
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 = (FLOAT32)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 = (WORD64)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] = (WORD32)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] = (WORD32)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;
}
/**
* ixheaacd_scale_adjust
*
* \brief Scale adjust process
*
* \param [in/out] samples
* \param [in] qshift_adj
* \param [in] frame_len
*
* \return WORD32
*
*/
VOID ixheaacd_scale_adjust(WORD32 *samples, UWORD32 frame_len,
WORD8 *qshift_adj, WORD num_channels) {
UWORD32 i;
WORD32 j;
for (i = 0; i < frame_len; i++) {
for (j = 0; j < num_channels; j++) {
WORD32 gain_t = (WORD32)(1 << *(qshift_adj + j));
samples[i * num_channels + j] = (samples[i * num_channels + j] * gain_t);
}
}
}