Files
BPI-CM9-Linux/aml-comp/multimedia/aml_audio_hal/utils/aml_audio_limiter.c
T
2026-08-24 10:03:24 +08:00

798 lines
25 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (C) 2025 Amlogic Corporation.
*
* 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.
*/
#define LOG_TAG "audio_hw_limiter"
#include <math.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <system/audio.h>
#include <audio_utils/primitives.h>
#include "aml_malloc_debug.h"
#include "aml_dump_debug.h"
#include "audio_data_process.h"
#include "aml_audio_limiter.h"
#include "aml_volume_utils.h"
#include "aml_audio_log.h"
#include "format.h"
// NTS AUDIO-LIMITER-HEAAC-HEAAC :
// will detect sample max vule, left small room to avoid reach clipping detect range.
#define LIMITER_INT32_0DB ((INT32_MAX - 128 * INT16_MAX) & 0xFFFF0000)
typedef union {
int32_t i;
float f;
} limiter_value;
typedef enum limiter_ease_type {
LimEase_Linear = 0, // small gain
LimEase_InOutQuad, // large gain
LimEase_InOutCubic, // not used it
LimEase_InOutQuart, // not used it
LimEase_Max
} limiter_ease_type_e;
typedef struct limiter_curve {
limiter_ease_type_e type;
float *data_buf_ptr;
size_t data_buf_size;
int data_len;
} limiter_curve_st;
typedef struct aml_audio_limiter {
int enable;
audio_format_t format;
int process_frames;
limiter_value last_sample_max;
float curr_gain;
float last_gain;
float peak_float;
int32_t peak_int32;
float release_factor;
float response_factor;
int release_delay;
int release_delay_count;
// data limit gain apply part
// input data --> delay_buf -> output
//float pre_gain;
float post_gain;
void *delay_buf_ptr;
void *src_temp_buf;
size_t src_temp_buf_size;
size_t delay_buf_size;
// when process_frames is small,
// use these member to improve waveform
float average_gain;
float out_curr_gain;
float out_last_gain;
int out_lazy_count;
limiter_curve_st curves[LimEase_Max];
} aml_audio_limiter_st;
// function declare
static int limiter_gen_curve_data(aml_audio_limiter_st *lim, limiter_ease_type_e ease_type, int proc_frames);
bool is_float_equal(float a, float b)
{
const float PRECISION = 1e-06;
return (fabs(a - b) < PRECISION);
}
void* aml_audio_limiter_new(audio_format_t data_format)
{
aml_audio_limiter_st *limiter = NULL;
limiter = (aml_audio_limiter_st *)aml_audio_calloc(1, sizeof(aml_audio_limiter_st));
if (limiter == NULL) {
ALOGE("%s malloc limiter failed!", __func__);
return NULL;
}
if (data_format != AUDIO_FORMAT_PCM_32_BIT && data_format != AUDIO_FORMAT_PCM_FLOAT && data_format != AUDIO_FORMAT_PCM_16_BIT) {
ALOGE("%s not support data_format %d", __func__, data_format);
aml_audio_free(limiter);
limiter = NULL;
return NULL;
}
if (data_format == AUDIO_FORMAT_PCM_16_BIT) {
data_format = AUDIO_FORMAT_PCM_32_BIT;
}
if (data_format == AUDIO_FORMAT_PCM_FLOAT) {
limiter->last_sample_max.f = 0.0f;
} else {
limiter->last_sample_max.i = 0;
}
limiter->format = data_format;
limiter->curr_gain = 1.0f;
limiter->last_gain = 1.0f;
limiter->out_curr_gain = 1.0f;
limiter->out_last_gain = 1.0f;
limiter->peak_float = 1.0f;
limiter->peak_int32 = LIMITER_INT32_0DB;
limiter->average_gain = 0.0f;
limiter->post_gain = 1.0f;
limiter->delay_buf_ptr = NULL;
limiter->delay_buf_size = 0;
limiter->src_temp_buf = NULL;
limiter->src_temp_buf_size = 0;
ALOGI("%s success ,limiter->format =%d", __func__,limiter->format);
return limiter;
}
void aml_audio_limiter_free(void *limiter)
{
int i = 0;
limiter_curve_st *p_curve = NULL;
aml_audio_limiter_st *lim = (aml_audio_limiter_st *)limiter;
if (lim == NULL) {
ALOGE("%s limiter is NULL", __func__);
return;
}
if (lim->src_temp_buf != NULL) {
ALOGI("%s free delay_buf_ptr %p, bytes %zu", __func__, lim->delay_buf_ptr, lim->src_temp_buf_size);
aml_audio_free(lim->src_temp_buf);
lim->src_temp_buf = NULL;
lim->src_temp_buf_size = 0;
}
if (lim->delay_buf_ptr != NULL) {
ALOGI("%s free delay_buf_ptr %p, bytes %zu", __func__, lim->delay_buf_ptr, lim->delay_buf_size);
aml_audio_free(lim->delay_buf_ptr);
lim->delay_buf_ptr = NULL;
lim->delay_buf_size = 0;
}
for (i = 0; i < LimEase_Max; i++) {
p_curve = &lim->curves[i];
if (p_curve->data_buf_ptr) {
ALOGI("%s free curves type %d, bytes %zu", __func__, p_curve->type, p_curve->data_buf_size);
aml_audio_free(p_curve->data_buf_ptr);
p_curve->data_buf_ptr = NULL;
p_curve->data_buf_size = 0;
p_curve->data_len = 0;
}
}
lim->enable = 0;
aml_audio_free(lim);
ALOGI("%s success", __func__);
}
static void limiter_smooth_output_gain(aml_audio_limiter_st *lim)
{
int percent = 0;
float gain_step = 0.0f;
float last_average = 0.0f;
float diff_value = 0.0f;
float out_curr_gain = 0.0f;
const float AVERAGE_FACTOR = 0.05;
if (lim == NULL || lim->process_frames <= 0) return;
// when process_frames >=256 frames, output gain is good enough to handle extreme case.
if (lim->process_frames >= 256) {
lim->out_last_gain = lim->out_curr_gain;
lim->out_curr_gain = lim->curr_gain;
return;
}
lim->out_last_gain = lim->out_curr_gain;
out_curr_gain = lim->curr_gain;
if (is_float_equal(lim->last_gain, 1.0f) && is_float_equal(lim->curr_gain, 1.0f)) {
lim->out_curr_gain = 1.0f;
lim->average_gain = 0;
lim->out_lazy_count = 0;
return;
}
last_average = lim->average_gain;
gain_step = (lim->curr_gain - lim->last_gain) / lim->process_frames;
lim->average_gain = lim->average_gain * (1 - AVERAGE_FACTOR) + lim->curr_gain * AVERAGE_FACTOR;
diff_value = lim->average_gain - last_average;
percent = (diff_value * 100 / AVERAGE_FACTOR);
// sinetone sample is stable, avoid tiny jitter
//ALOGV("%s percent %d, __gain_step %f, ", __func__, percent, gain_step);
if (percent == 0) {
if (gain_step > 0.000001) {
lim->out_lazy_count += 1;
} else if (gain_step < 0) {
lim->out_lazy_count = 0;
}
if (gain_step > 0.000001 && lim->out_lazy_count <= 2 && (lim->curr_gain - lim->last_gain < 0.005)) {
out_curr_gain = lim->out_last_gain;
}
}
lim->out_curr_gain = out_curr_gain;
}
static float limiter_find_sample_max_float(const float *buffer, const int count)
{
int i = 0;
float sample_max = 0.0f;
float sample_abs = 0.0f;
for (i = 0; i < count; i++) {
sample_abs = (buffer[i] < 0 ? -(buffer[i]) : buffer[i]);
if (sample_max < sample_abs) {
sample_max = sample_abs;
}
}
return sample_max;
}
static int32_t limiter_find_sample_max_int32(const int32_t *buffer, const int count)
{
int i = 0;
int32_t sample_max = 0;
int32_t sample_abs = 0;
for (i = 0; i < count; i++) {
sample_abs = (buffer[i] < 0 ? -(buffer[i]) : buffer[i]);
if (sample_max < sample_abs) {
sample_max = sample_abs;
}
}
return sample_max;
}
static void limiter_calculate_gain(void *limiter, const void *buffer, const int sample_count)
{
float gain_new = 1.0f;
float *float_buf = (float *)buffer;
int *int32_buf = (int *)buffer;
const float PRECISION = 1e-04;
aml_audio_limiter_st *lim = (aml_audio_limiter_st *)limiter;
int debug_value = get_debug_value(AML_DEBUG_AUDIOHAL_LIMITER);
float f_sample_max = 0.0f;
float f_current_max = 0.0f;
int i_sample_max = 0;
int i_current_max = 0;
if (lim == NULL || lim->enable == 0) {
return;
}
// find out the max sample value
if (lim->format == AUDIO_FORMAT_PCM_FLOAT) {
f_current_max = limiter_find_sample_max_float(float_buf, sample_count);
f_sample_max = f_current_max;
if (f_sample_max < lim->last_sample_max.f) {
f_sample_max = lim->last_sample_max.f;
}
lim->last_sample_max.f = f_current_max;
if (debug_value) {
ALOGI("%s f_current_max %f, f_sample_max %f", __func__, f_current_max, f_sample_max);
}
} else {
i_current_max = limiter_find_sample_max_int32(int32_buf, sample_count);
i_sample_max = i_current_max;
if (i_sample_max < lim->last_sample_max.i) {
i_sample_max = lim->last_sample_max.i;
}
lim->last_sample_max.i = i_current_max;
if (debug_value) {
ALOGI("%s i_current_max %d, i_sample_max %d", __func__, i_current_max, i_sample_max);
}
}
lim->last_gain = lim->curr_gain;
// limit output data level
if (lim->format == AUDIO_FORMAT_PCM_FLOAT) {
if (f_sample_max > lim->peak_float && f_sample_max != 0) {
gain_new = lim->peak_float / f_sample_max;
}
} else {
if (i_sample_max > lim->peak_int32 && i_sample_max != 0) {
gain_new = ((float)lim->peak_int32) / i_sample_max;
}
}
// calculate a proper gain value
if (!is_float_equal(gain_new, 1.0f) || !is_float_equal(lim->last_gain, 1.0f)) {
if (gain_new <= lim->curr_gain) {
lim->curr_gain = gain_new;
lim->release_delay_count = 0;
} else {
if (lim->release_delay_count < lim->release_delay) {
lim->release_delay_count += 1;
} else {
//slow release time
lim->curr_gain += (gain_new - lim->curr_gain) * lim->release_factor;
}
}
}
if (lim->curr_gain > 1.0f) {
lim->curr_gain = 1.0f;
}
if ((fabs(lim->curr_gain - 1.0f) < PRECISION)) {
// Avoid gain value very close to 0.9999(0.0001)
lim->curr_gain = 1.0f;
}
if (debug_value) {
ALOGI("%s lim %p curr_gain %f", __func__, lim, lim->curr_gain);
}
// Patch for algorithm shortcoming
limiter_smooth_output_gain(lim);
return;
}
static inline void limiter_get_gain(aml_audio_limiter_st *limiter, float *last_gain, float *curr_gain)
{
*last_gain = limiter->out_last_gain;
*curr_gain = limiter->out_curr_gain;
}
static float limiter_calculate_factor(int time_ms, int proc_frames, int sample_rate)
{
/*
* Y = Y * factor + X * (1 - factor)
* Yn − X = (factor)^n ∗(Y0 - X)
* (factor)^n = (Yn - X) / (Y0 − X)
* (factor)^n < target
*
* count * log(factor) < log(target)
* log(factor) < log(target) / count
*
* factor = exp(log(target) / count)
*/
double factor = 1.0f;
float count = (float)time_ms * sample_rate / (proc_frames * 1000);
const float TARGET = 5.0f / 100; // Diff 0.05 : is regarded attack or release action complete
ALOGI("%s : count %.04f, proc_frames %d, sample_rate %d, time_ms %d, target %f", __func__,
count, proc_frames, sample_rate, time_ms, TARGET);
factor = exp(log(TARGET) / count);
// arrvie 1/2 X :
int half_index = log(1 - 0.5) / log(factor);
ALOGI("%s : arrive half of target = %d\n", __func__, half_index);
return factor;
}
#if 0
//for debug
static void limiter_verify_factor(float factor, int count)
{
float X = 2.0f;
float Y = 1.0f;
int i = 0;
for (i = 0; i < count; i++) {
ALOGD("%f", Y);
Y = Y * factor + X * (1.0f - factor);
}
}
#endif
int aml_audio_limiter_set(void *limiter, float peak_level, int attack_time, int release_time, int proc_frames, limiter_config_mode_e limiter_type)
{
char info_buf[32];
aml_audio_limiter_st *lim = (aml_audio_limiter_st *)limiter;
if (lim == NULL) {
return -1;
}
if (attack_time < 30) {
attack_time = 30;
} else if (attack_time > 300) {
attack_time = 300;
}
if (release_time < 60) {
release_time = 60;
} else if (release_time > 600) {
release_time = 600;
}
if (peak_level < 0.0f || peak_level > 1.0f) {
ALOGE("%s fail, invalid peak_level %f !", __func__, peak_level);
return -1;
}
lim->release_factor = limiter_calculate_factor(release_time, proc_frames, 48000);
lim->response_factor = limiter_calculate_factor(attack_time, proc_frames, 48000);
lim->process_frames = proc_frames;
lim->release_delay = -1;
switch (proc_frames) {
case 384:
lim->release_delay = 1;
break;
case 256:
lim->release_delay = 2;
break;
case 120:
case 128:
lim->release_delay = 3;
break;
case 64:
lim->release_delay = 6;
break;
default:
break;
}
if (lim->release_delay <= 0) {
ALOGE("%s fail, not support proc_frames %d !", __func__, proc_frames);
return -1;
}
//limiter_gen_curve_data(lim, LimEase_Linear, proc_frames);
limiter_gen_curve_data(lim, LimEase_InOutQuad, proc_frames);
memset(info_buf, 0, sizeof(info_buf));
if (lim->format == AUDIO_FORMAT_PCM_FLOAT) {
lim->peak_float = peak_level;
snprintf(info_buf, sizeof(info_buf)-1, "float %.03f", peak_level);
} else {
if (is_float_equal(peak_level, 0.125)) {
lim->peak_int32 = LIMITER_INT32_0DB / 8;
} else if (is_float_equal(peak_level, 0.25)) {
lim->peak_int32 = LIMITER_INT32_0DB / 4;
} else if (is_float_equal(peak_level, 0.5)) {
lim->peak_int32 = LIMITER_INT32_0DB / 2;
} else {
lim->peak_int32 = LIMITER_INT32_0DB * peak_level;
}
snprintf(info_buf, sizeof(info_buf)-1, "int32 %d", lim->peak_int32);
}
//lim->pre_gain = peak_level;
if (limiter_type == AML_LIMITER) {
lim->post_gain = 1.0f / peak_level; //Use limiter
} else {
lim->post_gain = 1.0f; //Use limiter as DRC
}
lim->average_gain = 1.0f;
lim->out_lazy_count = 0;
ALOGI("%s release_factor = %f(release_time %d ms), process_frames = %d, release_delay = %d", __func__,
lim->release_factor, release_time, lim->process_frames, lim->release_delay);
ALOGI("%s peak_level: %f (dB %.03f, %s), post_gain %.03f,\n", __func__,
peak_level, AmplToDb(peak_level), info_buf, lim->post_gain);
return 0;
}
static inline float limiter_ease_next(limiter_ease_type_e ease, float t, float b, float c, float d)
{
float result = 0.0f;
switch (ease) {
case LimEase_Linear:
result = (c * t) / d + b;
break;
case LimEase_InOutQuad:
if ((t /= d / 2) < 1) {
result = ((c / 2) * (t * t)) + b;
} else {
--t;
result = -c / 2 * (((t - 2) * t) - 1) + b;
}
break;
case LimEase_InOutCubic:
if ((t /= d / 2) < 1) {
result = c / 2 * t * t * t + b;
} else {
t -= 2;
result = c / 2 * (t * t * t + 2) + b;
}
break;
case LimEase_InOutQuart:
if ((t /= d / 2) < 1) {
result = c / 2 * t * t * t * t + b;
} else {
t -= 2;
result = -c / 2 * (t * t * t * t - 2) + b;
}
break;
default:
break;
}
return result;
}
static int limiter_gen_curve_data(aml_audio_limiter_st *lim, limiter_ease_type_e ease_type, int proc_frames)
{
int i = 0;
int ret = 0;
int ease_frames = proc_frames;
float vol_delta = 1.0f;
float start_volume = 0.0f;
float current_volume = 0.0f;
float ease_frames_elapsed = 0.0f;
limiter_curve_st *p_curve = NULL;
size_t req_buf_size = 0;
if (ease_type < 0 || ease_type >= LimEase_Max) {
ALOGE("%s : invalid ease_type %d", __func__, ease_type);
return -1;
}
p_curve = &lim->curves[ease_type];
req_buf_size = sizeof(float) * proc_frames;
ret = aml_audio_check_and_realloc((void **)&p_curve->data_buf_ptr, &p_curve->data_buf_size, req_buf_size);
if ((ret != 0) || (p_curve->data_buf_ptr == NULL)) {
ALOGE("allocate data_buf_ptr(%zu bytes) failed", req_buf_size);
return -1;
}
memset(p_curve->data_buf_ptr, 0, p_curve->data_buf_size);
for (i = 0; i < proc_frames; i++) {
current_volume = limiter_ease_next(ease_type, ease_frames_elapsed, start_volume, vol_delta, ease_frames);
p_curve->data_buf_ptr[i] = current_volume;
ease_frames_elapsed++;
}
p_curve->type = ease_type;
p_curve->data_len = proc_frames;
return 0;
}
static inline float * get_curver_data(aml_audio_limiter_st *lim, limiter_ease_type_e ease_type, int proc_frames)
{
float *p_curve_data = lim->curves[ease_type].data_buf_ptr;
if (p_curve_data == NULL || lim->curves[ease_type].data_len < proc_frames) {
if (limiter_gen_curve_data(lim, ease_type, proc_frames) != 0) {
ALOGE("%s limiter_gen_curve_data type %d fail !", __func__, ease_type);
return NULL;
}
p_curve_data = lim->curves[ease_type].data_buf_ptr;
}
return p_curve_data;
}
static void limiter_apply_gain_fade(aml_audio_limiter_st *lim, float last_gain, float curr_gain, void *buf, int frame_num, int channels, int format)
{
int32_t *input32 = (int32_t *)buf;
float *input_float = (float *)buf;
unsigned int i = 0, j= 0;
float gain_step = 0.0f;
float next_gain = 1.0f;
float diff_gain = 0.0f;
float *p_fade_curve = NULL;
int debug_value = get_debug_value(AML_DEBUG_AUDIOHAL_LIMITER);
if (buf == NULL || channels == 0 || frame_num == 0) {
return;
}
p_fade_curve = get_curver_data(lim, LimEase_InOutQuad, frame_num);
if (p_fade_curve == NULL) {
ALOGE("%s p_fade_curve is NULL", __func__);
return;
}
if (!is_float_equal(last_gain, curr_gain)) {
gain_step = (curr_gain - last_gain)/frame_num;
diff_gain = curr_gain - last_gain;
}
if (format == AUDIO_FORMAT_PCM_32_BIT) {
for (i = 0; i < frame_num; i++) {
//next_gain = last_gain + i * gain_step;
next_gain = last_gain + diff_gain * p_fade_curve[i];
for (j = 0; j < channels; j ++) {
input32[i * channels + j] = clamp32((int64_t)(next_gain * input32[i * channels + j]));
}
}
} else if (format == AUDIO_FORMAT_PCM_FLOAT) {
for (i = 0; i < frame_num; i++) {
//next_gain = last_gain + i * gain_step;
next_gain = last_gain + diff_gain * p_fade_curve[i];
for (j = 0; j < channels; j ++) {
input_float[i * channels + j] = clamp_float((next_gain * input_float[i * channels + j]));
}
}
}
if (debug_value) {
char info_buf[32];
memset(info_buf, 0, sizeof(info_buf));
if (format == AUDIO_FORMAT_PCM_32_BIT) {
int sample_max = limiter_find_sample_max_int32(buf, frame_num * channels);
snprintf(info_buf, sizeof(info_buf)-1, "%d, %.03f", sample_max, ((float)sample_max)/INT32_MAX);
} else if (format == AUDIO_FORMAT_PCM_FLOAT) {
float sample_max = limiter_find_sample_max_float(buf, frame_num * channels);
snprintf(info_buf, sizeof(info_buf)-1, "%.03f", sample_max);
}
ALOGI("%s %p final sample_max %s", __func__, lim, info_buf);
}
return;
}
int aml_audio_limiter_process(void *limiter,
void *dst_buf, const audio_format_t dst_format,
void *src_buf, audio_format_t src_format,
const int channels, const int frame_num)
{
int ret = 0;
int block_frames = 0;
int block_samples = 0;
int handle_samples = 0;
const int sample_size = 4;
size_t block_bytes = 0;
aml_audio_limiter_st *lim = (aml_audio_limiter_st *)limiter;
void *curr_src_ptr = NULL;
int debug_value = get_debug_value(AML_DEBUG_AUDIOHAL_LIMITER);
int in_frames = frame_num;
int in_samples = in_frames * channels;
int in_bytes = in_samples * sample_size;
float last_gain = 1.0f;
float curr_gain = 1.0f;
float last_boost = 1.0f;
float curr_boost = 1.0f;
if (limiter == NULL || dst_buf == NULL || src_buf == NULL || channels <= 0 || frame_num <= 0) {
ALOGE("%s invalid param (limiter %p, buffer (%p, %p), channels %d, frame_num %d)", __func__,
limiter, dst_buf, src_buf, channels, frame_num);
return -1;
}
if (!lim->enable) {
ALOGV("%s : limiter is not enable !", __func__);
return -1;
}
ret = aml_audio_check_and_realloc(&lim->src_temp_buf, &lim->src_temp_buf_size, in_bytes);
if ((ret != 0) || (lim->src_temp_buf == NULL)) {
AM_LOGE("allocate src_temp_buf_ptr(%d bytes) failed", in_bytes);
return -1;
}
if (src_format == AUDIO_FORMAT_PCM_16_BIT) {
memcpy_by_audio_format(lim->src_temp_buf, lim->format, src_buf, AUDIO_FORMAT_PCM_16_BIT, in_samples);
src_format = AUDIO_FORMAT_PCM_32_BIT;;
} else {
memcpy_by_audio_format(lim->src_temp_buf, src_format, src_buf, src_format, in_samples);
}
if (lim->format != src_format) {
ALOGE("%s limiter format(%d) does NOT match src format(%d)", __func__, lim->format, src_format);
return -1;
}
if ((frame_num % lim->process_frames) != 0) {
ALOGE("%s not support frame_num %d (limiter process_frames %d)", __func__, frame_num, lim->process_frames);
return -1;
}
block_frames = lim->process_frames;
block_samples = block_frames * channels;
block_bytes = block_samples * sample_size;
ret = aml_audio_check_and_realloc(&lim->delay_buf_ptr, &lim->delay_buf_size, block_bytes);
if ((ret != 0) || (lim->delay_buf_ptr == NULL)) {
AM_LOGE("allocate delay_buf_ptr(%zu bytes) failed", block_bytes);
if (lim->src_temp_buf != NULL) {
aml_audio_free(lim->src_temp_buf);
lim->src_temp_buf = NULL;
lim->src_temp_buf_size = 0;
}
return -1;
}
for (handle_samples = 0; handle_samples < frame_num * channels; handle_samples += block_samples) {
curr_src_ptr = (uint8_t *)lim->src_temp_buf + (handle_samples * audio_bytes_per_sample(lim->format));
// calculate gain
if (debug_value) {
char name[64] = {0};
snprintf(name, 64, "audio_before_limiter_cal_%s_lim_%p.pcm", src_format == AUDIO_FORMAT_PCM_32_BIT ? "int32":"float", lim);
aml_dump_audio_bitstreams(name, curr_src_ptr, block_samples *sizeof(lim->format));
}
limiter_calculate_gain(lim, curr_src_ptr, block_samples);
limiter_get_gain(lim, &last_gain, &curr_gain);
// apply boost gain to delay buffer
last_boost = last_gain * lim->post_gain;
curr_boost = curr_gain * lim->post_gain;
limiter_apply_gain_fade(lim, last_boost, curr_boost, lim->delay_buf_ptr, block_frames, channels, lim->format);
// output delay buffer data, then move input buffer data into it
if (lim->format == dst_format) {
memcpy(dst_buf, lim->delay_buf_ptr, block_bytes);
} else {
memcpy_by_audio_format(dst_buf, dst_format, lim->delay_buf_ptr, lim->format, block_samples);
}
memcpy(lim->delay_buf_ptr, curr_src_ptr, block_bytes);
// update destination and source buffer pointer
dst_buf = (uint8_t *)dst_buf + (block_samples * audio_bytes_per_sample(dst_format));
}
return 0;
}
int aml_audio_limiter_enable(void *limiter, bool enable)
{
aml_audio_limiter_st *lim = (aml_audio_limiter_st *)limiter;
if (lim == NULL) {
ALOGE("%s fail, limiter is NULL", __func__);
return -1;
}
if (lim->enable != enable) {
aml_audio_limiter_flush(limiter);
}
lim->enable = enable;
ALOGI("%s enable=%d ok", __func__, enable);
return 0;
}
int aml_audio_limiter_get_delay(void *limiter)
{
aml_audio_limiter_st *lim = (aml_audio_limiter_st *)limiter;
if (lim == NULL || (!lim->enable)) {
return 0;
}
return lim->process_frames;
}
static void limiter_reset(aml_audio_limiter_st *limiter)
{
limiter->curr_gain = 1.0f;
limiter->last_gain = 1.0f;
if (limiter->format == AUDIO_FORMAT_PCM_FLOAT) {
limiter->last_sample_max.f = 0.0f;
} else {
limiter->last_sample_max.i = 0;
}
limiter->release_delay_count = 0;
limiter->average_gain = 1.0f;
limiter->out_curr_gain = 1.0f;
limiter->out_last_gain = 1.0f;
limiter->out_lazy_count = 0;
}
int aml_audio_limiter_flush(void *limiter)
{
aml_audio_limiter_st *lim = (aml_audio_limiter_st *)limiter;
if (lim == NULL) {
return -1;
}
if (lim->delay_buf_ptr) {
memset(lim->delay_buf_ptr, 0, lim->delay_buf_size);
}
if (lim->src_temp_buf) {
memset(lim->src_temp_buf, 0, lim->src_temp_buf_size);
}
limiter_reset(lim);
ALOGI("%s ok", __func__);
return 0;
}