/* * 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 #include #include #include #include #include #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; }