diff options
Diffstat (limited to 'decoder/ixheaacd_peak_limiter.c')
-rw-r--r-- | decoder/ixheaacd_peak_limiter.c | 206 |
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; +} |