summaryrefslogtreecommitdiffstats
path: root/audio/fir_filter.c
diff options
context:
space:
mode:
authorJoonas Kylmälä <joonas.kylmala@iki.fi>2020-11-28 11:32:28 -0500
committerJoonas Kylmälä <joonas.kylmala@iki.fi>2020-11-28 11:32:28 -0500
commitdb0f5726f587e6d4e942d358115853ac5e11b55d (patch)
tree5b9aab11ce353ff7bca90856495f67e9e50e463d /audio/fir_filter.c
parent2406ae1558f0c5429f88ab97c7c7cdf42d6269a4 (diff)
downloaddevice_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.c154
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));
+}