diff options
author | Joonas Kylmälä <joonas.kylmala@iki.fi> | 2020-11-28 11:32:28 -0500 |
---|---|---|
committer | Joonas Kylmälä <joonas.kylmala@iki.fi> | 2020-11-28 11:32:28 -0500 |
commit | db0f5726f587e6d4e942d358115853ac5e11b55d (patch) | |
tree | 5b9aab11ce353ff7bca90856495f67e9e50e463d /audio/fir_filter.c | |
parent | 2406ae1558f0c5429f88ab97c7c7cdf42d6269a4 (diff) | |
download | device_samsung_midas-common-db0f5726f587e6d4e942d358115853ac5e11b55d.tar.gz device_samsung_midas-common-db0f5726f587e6d4e942d358115853ac5e11b55d.tar.bz2 device_samsung_midas-common-db0f5726f587e6d4e942d358115853ac5e11b55d.zip |
audio: update module to latest upstream version
This updates the audio module to version
e24372e861c14654a4eb9449dd3d0a615522f084
from https://android.googlesource.com/device/linaro/dragonboard
Signed-off-by: Joonas Kylmälä <joonas.kylmala@iki.fi>
Diffstat (limited to 'audio/fir_filter.c')
-rw-r--r-- | audio/fir_filter.c | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/audio/fir_filter.c b/audio/fir_filter.c new file mode 100644 index 0000000..c648fc0 --- /dev/null +++ b/audio/fir_filter.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2020 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. + */ + +#define LOG_TAG "audio_hw_fir_filter" +//#define LOG_NDEBUG 0 + +#include <assert.h> +#include <audio_utils/primitives.h> +#include <errno.h> +#include <inttypes.h> +#include <log/log.h> +#include <malloc.h> +#include <string.h> + +#include "fir_filter.h" + +#ifdef __ARM_NEON +#include "arm_neon.h" +#endif /* #ifdef __ARM_NEON */ + +fir_filter_t* fir_init(uint32_t channels, fir_filter_mode_t mode, uint32_t filter_length, + uint32_t input_length, int16_t* coeffs) { + if ((channels == 0) || (filter_length == 0) || (coeffs == NULL)) { + ALOGE("%s: Invalid channel count, filter length or coefficient array.", __func__); + return NULL; + } + + fir_filter_t* fir = (fir_filter_t*)calloc(1, sizeof(fir_filter_t)); + if (fir == NULL) { + ALOGE("%s: Unable to allocate memory for fir_filter.", __func__); + return NULL; + } + + fir->channels = channels; + fir->filter_length = filter_length; + /* Default: same filter coeffs for all channels */ + fir->mode = FIR_SINGLE_FILTER; + uint32_t coeff_bytes = fir->filter_length * sizeof(int16_t); + if (mode == FIR_PER_CHANNEL_FILTER) { + fir->mode = FIR_PER_CHANNEL_FILTER; + coeff_bytes = fir->filter_length * fir->channels * sizeof(int16_t); + } + + fir->coeffs = (int16_t*)malloc(coeff_bytes); + if (fir->coeffs == NULL) { + ALOGE("%s: Unable to allocate memory for FIR coeffs", __func__); + goto exit_1; + } + memcpy(fir->coeffs, coeffs, coeff_bytes); + + fir->buffer_size = (input_length + fir->filter_length) * fir->channels; + fir->state = (int16_t*)malloc(fir->buffer_size * sizeof(int16_t)); + if (fir->state == NULL) { + ALOGE("%s: Unable to allocate memory for FIR state", __func__); + goto exit_2; + } + +#ifdef __ARM_NEON + ALOGI("%s: Using ARM Neon", __func__); +#endif /* #ifdef __ARM_NEON */ + + fir_reset(fir); + return fir; + +exit_2: + free(fir->coeffs); +exit_1: + free(fir); + return NULL; +} + +void fir_release(fir_filter_t* fir) { + if (fir == NULL) { + return; + } + free(fir->state); + free(fir->coeffs); + free(fir); +} + +void fir_reset(fir_filter_t* fir) { + if (fir == NULL) { + return; + } + memset(fir->state, 0, fir->buffer_size * sizeof(int16_t)); +} + +void fir_process_interleaved(fir_filter_t* fir, int16_t* input, int16_t* output, uint32_t samples) { + assert(fir != NULL); + + int start_offset = (fir->filter_length - 1) * fir->channels; + memcpy(&fir->state[start_offset], input, samples * fir->channels * sizeof(int16_t)); + // int ch; + bool use_2nd_set_coeffs = (fir->channels > 1) && (fir->mode == FIR_PER_CHANNEL_FILTER); + int16_t* p_coeff_A = &fir->coeffs[0]; + int16_t* p_coeff_B = use_2nd_set_coeffs ? &fir->coeffs[fir->filter_length] : &fir->coeffs[0]; + int16_t* p_output; + for (int ch = 0; ch < fir->channels; ch += 2) { + p_output = &output[ch]; + int offset = start_offset + ch; + for (int s = 0; s < samples; s++) { + int32_t acc_A = 0; + int32_t acc_B = 0; + +#ifdef __ARM_NEON + int32x4_t acc_vec = vdupq_n_s32(0); + for (int k = 0; k < fir->filter_length; k++, offset -= fir->channels) { + int16x4_t coeff_vec = vdup_n_s16(p_coeff_A[k]); + coeff_vec = vset_lane_s16(p_coeff_B[k], coeff_vec, 1); + int16x4_t input_vec = vld1_s16(&fir->state[offset]); + acc_vec = vmlal_s16(acc_vec, coeff_vec, input_vec); + } + acc_A = vgetq_lane_s32(acc_vec, 0); + acc_B = vgetq_lane_s32(acc_vec, 1); +#else + for (int k = 0; k < fir->filter_length; k++, offset -= fir->channels) { + int32_t input_A = (int32_t)(fir->state[offset]); + int32_t coeff_A = (int32_t)(p_coeff_A[k]); + int32_t input_B = (int32_t)(fir->state[offset + 1]); + int32_t coeff_B = (int32_t)(p_coeff_B[k]); + acc_A += (input_A * coeff_A); + acc_B += (input_B * coeff_B); + } +#endif /* #ifdef __ARM_NEON */ + + *p_output = clamp16(acc_A >> 15); + if (ch < fir->channels - 1) { + *(p_output + 1) = clamp16(acc_B >> 15); + } + /* Move to next sample */ + p_output += fir->channels; + offset += (fir->filter_length + 1) * fir->channels; + } + if (use_2nd_set_coeffs) { + p_coeff_A += (fir->filter_length << 1); + p_coeff_B += (fir->filter_length << 1); + } + } + memmove(fir->state, &fir->state[samples * fir->channels], + (fir->filter_length - 1) * fir->channels * sizeof(int16_t)); +} |