hal: Add support for Fluence Far Field Voice
Add support for 6mic Fluence pre-processing of
captured stream using Far Field Voice (FFV) algorithm.
Change-Id: I7a643014f3e090482db7772dce2fc812fd469b2d
diff --git a/hal/audio_extn/ffv.c b/hal/audio_extn/ffv.c
new file mode 100644
index 0000000..fa55b5a
--- /dev/null
+++ b/hal/audio_extn/ffv.c
@@ -0,0 +1,891 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "audio_hw_ffv"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+/*#define VERY_VERY_VERBOSE_LOGGING*/
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+#else
+#define ALOGVV(a...) do { } while(0)
+#endif
+
+#include <errno.h>
+#include <cutils/properties.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <cutils/str_parms.h>
+#include <cutils/log.h>
+#include <pthread.h>
+#include <sys/resource.h>
+
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+
+#include "ffv_interface.h"
+
+#define AUDIO_PARAMETER_FFV_MODE_ON "ffvOn"
+#define AUDIO_PARAMETER_FFV_SPLIT_EC_REF_DATA "ffv_split_ec_ref_data"
+#define AUDIO_PARAMETER_FFV_EC_REF_CHANNEL_COUNT "ffv_ec_ref_channel_count"
+#define AUDIO_PARAMETER_FFV_EC_REF_DEVICE "ffv_ec_ref_dev"
+#define AUDIO_PARAMETER_FFV_CHANNEL_INDEX "ffv_channel_index"
+
+#define FFV_LIB "libffv.so"
+#define FFV_CONFIG_FILE_PATH "/etc/BF_1out.cfg"
+#define FFV_SAMPLING_RATE_16000 16000
+#define FFV_EC_REF_LOOPBACK_DEVICE_MONO "ec-ref-loopback-mono"
+#define FFV_EC_REF_LOOPBACK_DEVICE_STEREO "ec-ref-loopback-stereo"
+
+#define FFV_CHANNEL_MODE_MONO 1
+#define FFV_CHANNEL_MODE_STEREO 2
+#define FFV_CHANNEL_MODE_HEX 6
+#define FFV_CHANNEL_MODE_OCT 8
+
+#define FFV_PCM_BUFFER_DURATION_MS 160
+#define FFV_PCM_PERIOD_COUNT (8)
+#define FFV_PCM_PERIOD_SIZE \
+ ((((FFV_SAMPLING_RATE_16000 * FFV_PCM_BUFFER_DURATION_MS) \
+ /(FFV_PCM_PERIOD_COUNT * 1000)) + 0x1f) & ~0x1f)
+
+#define ALIGN(number, align) \
+ ((number + align - 1) & ~(align - 1))
+#define CALCULATE_PERIOD_SIZE(duration_ms, sample_rate, period_cnt, align) \
+ (ALIGN(((sample_rate * duration_ms) /(period_cnt * 1000)), align))
+
+#define FFV_PCM_MAX_RETRY 10
+#define FFV_PCM_SLEEP_WAIT 1000
+
+#define DLSYM(handle, name, err) \
+do {\
+ const char* error; \
+ *(void**)&name##_fn = dlsym(handle, #name);\
+ if ((error = dlerror())) {\
+ ALOGE("%s: dlsym failed for %s error %s", __func__, #name, error);\
+ err = -ENODEV;\
+ }\
+} while(0)\
+
+/* uncomment to collect pcm dumps */
+//#define FFV_PCM_DUMP
+
+static FfvStatusType (*ffv_init_fn)(void** handle, int num_tx_in_ch,
+ int num_out_ch, int num_ec_ref_ch, int frame_len, int sample_rate,
+ const char *config_file_name, char *svaModelBuffer,
+ uint32_t svaModelSize, int* totMemSize);
+static void (*ffv_deinit_fn)(void* handle);
+static void (*ffv_process_fn)(void *handle, const int16_t *in_pcm,
+ int16_t *out_pcm, const int16_t *ec_ref_pcm);
+static int (*ffv_read_fn)(void* handle, int16_t *buf_pcm,
+ int max_buf_len);
+static FfvStatusType (*ffv_get_param_fn)(void *handle, char *params_buffer_ptr,
+ int param_id, int buffer_size, int *param_size_ptr);
+static FfvStatusType (*ffv_set_param_fn)(void *handle, char *params_buffer_ptr,
+ int param_id, int param_size);
+static FfvStatusType (*ffv_register_event_callback_fn)(void *handle,
+ ffv_event_callback_fn_t *fun_ptr);
+
+struct ffvmodule {
+ void *ffv_lib_handle;
+ unsigned char *in_buf;
+ unsigned int in_buf_size;
+ unsigned char *ec_ref_buf;
+ unsigned int ec_ref_buf_size;
+ unsigned char *split_in_buf;
+ unsigned int split_in_buf_size;
+ unsigned char *out_buf;
+ unsigned int out_buf_size;
+
+ struct pcm_config capture_config;
+ struct pcm_config out_config;
+ struct pcm_config ec_ref_config;
+
+ int ec_ref_pcm_id;
+ struct pcm *ec_ref_pcm;
+ int ec_ref_ch_cnt;
+ audio_devices_t ec_ref_dev;
+ bool split_ec_ref_data;
+
+ bool is_ffv_enabled;
+ bool buffers_allocated;
+ struct stream_in *in;
+ bool is_ffvmode_on;
+ void *handle;
+ pthread_mutex_t init_lock;
+ bool capture_started;
+ int target_ch_idx;
+
+#ifdef FFV_PCM_DUMP
+ FILE *fp_input;
+ FILE *fp_ecref;
+ FILE *fp_split_input;
+ FILE *fp_output;
+#endif
+};
+
+static struct ffvmodule ffvmod = {
+ .ffv_lib_handle = NULL,
+ .in_buf = NULL,
+ .in_buf_size = 0,
+ .ec_ref_buf = NULL,
+ .ec_ref_buf_size = 0,
+ .split_in_buf = NULL,
+ .split_in_buf_size = 0,
+ .out_buf = NULL,
+ .out_buf_size = 0,
+
+ .ec_ref_pcm = NULL,
+ .ec_ref_ch_cnt = 1,
+ .ec_ref_dev = AUDIO_DEVICE_OUT_SPEAKER,
+ .is_ffv_enabled = false,
+ .buffers_allocated = false,
+ .in = NULL,
+ .is_ffvmode_on = false,
+ .handle = NULL,
+ .capture_started = false,
+ .target_ch_idx = -1,
+};
+
+static struct pcm_config ffv_pcm_config = {
+ .channels = FFV_CHANNEL_MODE_MONO,
+ .rate = FFV_SAMPLING_RATE_16000,
+ .period_size = FFV_PCM_PERIOD_SIZE,
+ .period_count = FFV_PCM_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+};
+
+static int32_t ffv_init_lib()
+{
+ int status = 0;
+
+ if (ffvmod.ffv_lib_handle) {
+ ALOGE("%s: FFV library is already initialized", __func__);
+ return 0;
+ }
+
+ ffvmod.ffv_lib_handle = dlopen(FFV_LIB, RTLD_NOW);
+ if (!ffvmod.ffv_lib_handle) {
+ ALOGE("%s: Unable to open %s, error %s", __func__, FFV_LIB,
+ dlerror());
+ status = -ENOENT;
+ goto exit;
+ }
+
+ dlerror(); /* clear errors */
+ DLSYM(ffvmod.ffv_lib_handle, ffv_init, status);
+ if (status)
+ goto exit;
+ DLSYM(ffvmod.ffv_lib_handle, ffv_deinit, status);
+ if (status)
+ goto exit;
+ DLSYM(ffvmod.ffv_lib_handle, ffv_process, status);
+ if (status)
+ goto exit;
+ DLSYM(ffvmod.ffv_lib_handle, ffv_read, status);
+ if (status)
+ goto exit;
+ DLSYM(ffvmod.ffv_lib_handle, ffv_get_param, status);
+ if (status)
+ goto exit;
+ DLSYM(ffvmod.ffv_lib_handle, ffv_set_param, status);
+ if (status)
+ goto exit;
+ DLSYM(ffvmod.ffv_lib_handle, ffv_register_event_callback, status);
+ if (status)
+ goto exit;
+
+ return status;
+
+exit:
+ if (ffvmod.ffv_lib_handle)
+ dlclose(ffvmod.ffv_lib_handle);
+ ffvmod.ffv_lib_handle = NULL;
+
+ return status;
+}
+
+static int deallocate_buffers()
+{
+ if (ffvmod.in_buf) {
+ free(ffvmod.in_buf);
+ ffvmod.in_buf = NULL;
+ }
+
+ if (ffvmod.split_in_buf) {
+ free(ffvmod.split_in_buf);
+ ffvmod.split_in_buf = NULL;
+ }
+
+ if (ffvmod.ec_ref_buf) {
+ free(ffvmod.ec_ref_buf);
+ ffvmod.ec_ref_buf = NULL;
+ }
+
+ if (ffvmod.out_buf) {
+ free(ffvmod.out_buf);
+ ffvmod.out_buf = NULL;
+ }
+
+ ffvmod.buffers_allocated = false;
+ return 0;
+}
+
+static int allocate_buffers()
+{
+ int status = 0;
+
+ /* in_buf - buffer read from capture session */
+ ffvmod.in_buf_size = ffvmod.capture_config.period_size * ffvmod.capture_config.channels *
+ (pcm_format_to_bits(ffvmod.capture_config.format) >> 3);
+ ffvmod.in_buf = (unsigned char *)calloc(1, ffvmod.in_buf_size);
+ if (!ffvmod.in_buf) {
+ ALOGE("%s: ERROR. Can not allocate in buffer size %d", __func__, ffvmod.in_buf_size);
+ status = -ENOMEM;
+ goto error_exit;
+ }
+ ALOGD("%s: Allocated in buffer size bytes =%d",
+ __func__, ffvmod.in_buf_size);
+
+ /* ec_buf - buffer read from ec ref capture session */
+ ffvmod.ec_ref_buf_size = ffvmod.ec_ref_config.period_size * ffvmod.ec_ref_config.channels *
+ (pcm_format_to_bits(ffvmod.ec_ref_config.format) >> 3);
+ ffvmod.ec_ref_buf = (unsigned char *)calloc(1, ffvmod.ec_ref_buf_size);
+ if (!ffvmod.ec_ref_buf) {
+ ALOGE("%s: ERROR. Can not allocate ec ref buffer size %d",
+ __func__, ffvmod.ec_ref_buf_size);
+ status = -ENOMEM;
+ goto error_exit;
+ }
+ ALOGD("%s: Allocated ec ref buffer size bytes =%d",
+ __func__, ffvmod.ec_ref_buf_size);
+
+ if (ffvmod.split_ec_ref_data) {
+ ffvmod.split_in_buf_size = ffvmod.in_buf_size - ffvmod.ec_ref_buf_size;
+ ffvmod.split_in_buf = (unsigned char *)calloc(1, ffvmod.split_in_buf_size);
+ if (!ffvmod.split_in_buf) {
+ ALOGE("%s: ERROR. Can not allocate split in buffer size %d",
+ __func__, ffvmod.split_in_buf_size);
+ status = -ENOMEM;
+ goto error_exit;
+ }
+ ALOGD("%s: Allocated split in buffer size bytes =%d",
+ __func__, ffvmod.split_in_buf_size);
+ }
+
+ /* out_buf - output buffer from FFV + SVA library */
+ ffvmod.out_buf_size = ffvmod.out_config.period_size * ffvmod.out_config.channels *
+ (pcm_format_to_bits(ffvmod.out_config.format) >> 3);
+ ffvmod.out_buf = (unsigned char *)calloc(1, ffvmod.out_buf_size);
+ if (!ffvmod.out_buf) {
+ ALOGE("%s: ERROR. Can not allocate out buffer size %d", __func__, ffvmod.out_buf_size);
+ status = -ENOMEM;
+ goto error_exit;
+ }
+ ALOGD("%s: Allocated out buffer size bytes =%d",
+ __func__, ffvmod.out_buf_size);
+
+ ffvmod.buffers_allocated = true;
+ return 0;
+
+error_exit:
+ deallocate_buffers();
+ return status;
+}
+
+void audio_extn_ffv_update_enabled()
+{
+ char ffv_enabled[PROPERTY_VALUE_MAX] = "false";
+
+ property_get("ro.qc.sdk.audio.ffv", ffv_enabled, "0");
+ if (!strncmp("true", ffv_enabled, 4)) {
+ ALOGD("%s: ffv is supported", __func__);
+ ffvmod.is_ffv_enabled = true;
+ } else {
+ ALOGD("%s: ffv is not supported", __func__);
+ ffvmod.is_ffv_enabled = false;
+ }
+}
+
+bool audio_extn_ffv_get_enabled()
+{
+ ALOGV("%s: is_ffv_enabled:%d is_ffvmode_on:%d ", __func__, ffvmod.is_ffv_enabled, ffvmod.is_ffvmode_on);
+
+ if(ffvmod.is_ffv_enabled && ffvmod.is_ffvmode_on)
+ return true;
+
+ return false;
+}
+
+bool audio_extn_ffv_check_usecase(struct stream_in *in) {
+ int ret = false;
+ int channel_count = audio_channel_count_from_in_mask(in->channel_mask);
+ audio_devices_t devices = in->device;
+ audio_source_t source = in->source;
+
+ if ((audio_extn_ffv_get_enabled()) &&
+ (channel_count == 1) &&
+ (AUDIO_SOURCE_MIC == source) &&
+ ((AUDIO_DEVICE_IN_BUILTIN_MIC == devices) || (AUDIO_DEVICE_IN_BACK_MIC == devices)) &&
+ (in->format == AUDIO_FORMAT_PCM_16_BIT) &&
+ (in->sample_rate == FFV_SAMPLING_RATE_16000)) {
+ in->config.channels = channel_count;
+ in->config.period_count = FFV_PCM_PERIOD_COUNT;
+ in->config.period_size = FFV_PCM_PERIOD_SIZE;
+ ALOGD("%s: FFV enabled", __func__);
+ ret = true;
+ }
+ return ret;
+}
+
+int audio_extn_ffv_set_usecase(struct stream_in *in)
+{
+ int ret = -EINVAL;
+
+ if (audio_extn_ffv_check_usecase(in)) {
+ if (!audio_extn_ffv_stream_init(in)) {
+ ALOGD("%s: Created FFV session succesfully", __func__);
+ ret = 0;
+ } else {
+ ALOGE("%s: Unable to start FFV record session", __func__);
+ }
+ }
+ return ret;
+}
+
+struct stream_in *audio_extn_ffv_get_stream()
+{
+ return ffvmod.in;
+}
+
+void audio_extn_ffv_update_pcm_config(struct pcm_config *config)
+{
+ config->channels = ffvmod.capture_config.channels;
+ config->period_count = ffvmod.capture_config.period_count;
+ config->period_size = ffvmod.capture_config.period_size;
+}
+
+int32_t audio_extn_ffv_init(struct audio_device *adev)
+{
+ int ret = 0;
+
+ ret = ffv_init_lib();
+ if (ret)
+ ALOGE("%s: ERROR. ffv_init_lib ret %d", __func__, ret);
+
+ pthread_mutex_init(&ffvmod.init_lock, NULL);
+ return ret;
+}
+
+int32_t audio_extn_ffv_deinit()
+{
+ pthread_mutex_destroy(&ffvmod.init_lock);
+ if (ffvmod.ffv_lib_handle) {
+ dlclose(ffvmod.ffv_lib_handle);
+ ffvmod.ffv_lib_handle = NULL;
+ }
+ return 0;
+}
+
+int32_t audio_extn_ffv_stream_init(struct stream_in *in)
+{
+ uint32_t ret = -EINVAL;
+ int num_tx_in_ch, num_out_ch, num_ec_ref_ch;
+ int frame_len;
+ int sample_rate;
+ const char *config_file_path = FFV_CONFIG_FILE_PATH;
+ int total_mem_size;
+ FfvStatusType status_type;
+ const char *sm_buffer = "DISABLE_KEYWORD_DETECTION";
+ ffv_target_channel_index_param_t ch_index_param;
+ char *params_buffer_ptr = NULL;
+ int param_size = 0;
+ int param_id;
+
+ if (!audio_extn_ffv_get_enabled()) {
+ ALOGE("Rejecting FFV -- init is called without enabling FFV");
+ goto fail;
+ }
+
+ if (ffvmod.handle != NULL) {
+ ALOGV("%s: reinitializing ffv library", __func__);
+ audio_extn_ffv_stream_deinit();
+ }
+
+ ffvmod.capture_config = ffv_pcm_config;
+ ffvmod.ec_ref_config = ffv_pcm_config;
+ ffvmod.out_config = ffv_pcm_config;
+ /* configure capture session with 6/8 channels */
+ ffvmod.capture_config.channels = ffvmod.split_ec_ref_data ?
+ FFV_CHANNEL_MODE_OCT : FFV_CHANNEL_MODE_HEX;
+ ffvmod.capture_config.period_size =
+ CALCULATE_PERIOD_SIZE(FFV_PCM_BUFFER_DURATION_MS,
+ ffvmod.capture_config.rate,
+ FFV_PCM_PERIOD_COUNT, 32);
+
+ /* Update channels with ec ref channel count */
+ ffvmod.ec_ref_config.channels = ffvmod.ec_ref_ch_cnt;
+ ffvmod.ec_ref_config.period_size =
+ CALCULATE_PERIOD_SIZE(FFV_PCM_BUFFER_DURATION_MS,
+ ffvmod.ec_ref_config.rate,
+ FFV_PCM_PERIOD_COUNT, 32);
+ ret = allocate_buffers();
+ if (ret)
+ goto fail;
+
+ num_ec_ref_ch = ffvmod.ec_ref_config.channels;
+ num_tx_in_ch = ffvmod.split_ec_ref_data ?
+ (ffvmod.capture_config.channels - num_ec_ref_ch) :
+ ffvmod.capture_config.channels;
+ num_out_ch = ffvmod.out_config.channels;
+ frame_len = ffvmod.capture_config.period_size;
+ sample_rate = ffvmod.capture_config.rate;
+
+ ALOGD("%s: ec_ref_ch %d, tx_in_ch %d, out_ch %d, frame_len %d, sample_rate %d",
+ __func__, num_ec_ref_ch, num_tx_in_ch, num_out_ch, frame_len, sample_rate);
+ ALOGD("%s: config file path %s", __func__, config_file_path);
+ status_type = ffv_init_fn(&ffvmod.handle, num_tx_in_ch, num_out_ch, num_ec_ref_ch,
+ frame_len, sample_rate, config_file_path, sm_buffer, 0,
+ &total_mem_size);
+ if (status_type) {
+ ALOGE("%s: ERROR. ffv_init returned %d", __func__, status_type);
+ ret = -EINVAL;
+ goto fail;
+ }
+ ALOGD("%s: ffv_init success %p", __func__, ffvmod.handle);
+
+ /* set target channel index if received as part of setparams */
+ if (ffvmod.target_ch_idx != -1) {
+ ALOGD("%s: target channel index %d", __func__, ffvmod.target_ch_idx);
+ ch_index_param.target_chan_idx = ffvmod.target_ch_idx;
+ params_buffer_ptr = (char *)&ch_index_param;
+ param_size = sizeof(ch_index_param);
+ param_id = FFV_TARGET_CHANNEL_INDEX_PARAM;
+ status_type = ffv_set_param_fn(ffvmod.handle, params_buffer_ptr,
+ param_id, param_size);
+ if (status_type) {
+ ALOGE("%s: ERROR. ffv_set_param_fn ret %d", __func__, status_type);
+ ret = -EINVAL;
+ goto fail;
+ }
+ }
+
+ ffvmod.in = in;
+
+#ifdef FFV_PCM_DUMP
+ if (!ffvmod.fp_input) {
+ ALOGD("%s: Opening input dump file \n", __func__);
+ ffvmod.fp_input = fopen("/data/misc/audio/ffv_input.pcm", "wb");
+ }
+ if (!ffvmod.fp_ecref) {
+ ALOGD("%s: Opening ecref dump file \n", __func__);
+ ffvmod.fp_ecref = fopen("/data/misc/audio/ffv_ecref.pcm", "wb");
+ }
+ if (!ffvmod.fp_split_input && ffvmod.split_ec_ref_data) {
+ ALOGD("%s: Opening split input dump file \n", __func__);
+ ffvmod.fp_split_input = fopen("/data/misc/audio/ffv_split_input.pcm", "wb");
+ }
+ if (!ffvmod.fp_output) {
+ ALOGD("%s: Opening output dump file \n", __func__);
+ ffvmod.fp_output = fopen("/data/misc/audio/ffv_output.pcm", "wb");
+ }
+#endif
+ ALOGV("%s: exit", __func__);
+ return 0;
+
+fail:
+ audio_extn_ffv_stream_deinit();
+ return ret;
+}
+
+int32_t audio_extn_ffv_stream_deinit()
+{
+ ALOGV("%s: entry", __func__);
+
+#ifdef FFV_PCM_DUMP
+ if (ffvmod.fp_input)
+ fclose(ffvmod.fp_input);
+
+ if (ffvmod.fp_ecref)
+ fclose(ffvmod.fp_ecref);
+
+ if (ffvmod.fp_split_input)
+ fclose(ffvmod.fp_split_input);
+
+ if (ffvmod.fp_output)
+ fclose(ffvmod.fp_output);
+#endif
+
+ if (ffvmod.handle)
+ ffv_deinit_fn(ffvmod.handle);
+
+ if (ffvmod.buffers_allocated)
+ deallocate_buffers();
+
+ ffvmod.in = NULL;
+ ALOGV("%s: exit", __func__);
+ return 0;
+}
+
+snd_device_t audio_extn_ffv_get_capture_snd_device()
+{
+ if (ffvmod.capture_config.channels == FFV_CHANNEL_MODE_OCT) {
+ return SND_DEVICE_IN_HANDSET_8MIC;
+ } else if (ffvmod.capture_config.channels == FFV_CHANNEL_MODE_HEX) {
+ return SND_DEVICE_IN_HANDSET_6MIC;
+ } else {
+ ALOGE("%s: Invalid channels configured for capture", __func__);
+ return SND_DEVICE_NONE;
+ }
+}
+
+int audio_extn_ffv_init_ec_ref_loopback(struct audio_device *adev,
+ snd_device_t snd_device)
+{
+ struct audio_usecase *uc_info_tx = NULL;
+ snd_device_t in_snd_device;
+ char *params_buffer_ptr = NULL;
+ int param_id = FFV_RESET_AEC_PARAM;
+ int param_size = 0;
+ FfvStatusType status_type;
+ int ret = 0;
+
+ ALOGV("%s: entry", __func__);
+ /* notify library to reset AEC during each start */
+ status_type = ffv_set_param_fn(ffvmod.handle, params_buffer_ptr,
+ param_id, param_size);
+ if (status_type) {
+ ALOGE("%s: ERROR. ffv_set_param_fn ret %d", __func__, status_type);
+ return -EINVAL;
+ }
+
+ if (ffvmod.split_ec_ref_data) {
+ ALOGV("%s: Ignore ec ref loopback init", __func__);
+ return 0;
+ }
+
+ in_snd_device = platform_get_ec_ref_loopback_snd_device(ffvmod.ec_ref_ch_cnt);
+ uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+ if (!uc_info_tx) {
+ return -ENOMEM;
+ }
+
+ pthread_mutex_lock(&ffvmod.init_lock);
+ uc_info_tx->id = USECASE_AUDIO_EC_REF_LOOPBACK;
+ uc_info_tx->type = PCM_CAPTURE;
+ uc_info_tx->in_snd_device = in_snd_device;
+ uc_info_tx->out_snd_device = SND_DEVICE_NONE;
+ ffvmod.ec_ref_pcm = NULL;
+ list_add_tail(&adev->usecase_list, &uc_info_tx->list);
+ enable_snd_device(adev, in_snd_device);
+ enable_audio_route(adev, uc_info_tx);
+
+ ffvmod.ec_ref_pcm_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
+ if (ffvmod.ec_ref_pcm_id < 0) {
+ ALOGE("%s: Invalid pcm device for usecase (%d)",
+ __func__, uc_info_tx->id);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ ffvmod.ec_ref_pcm = pcm_open(adev->snd_card,
+ ffvmod.ec_ref_pcm_id,
+ PCM_IN, &ffvmod.ec_ref_config);
+ if (ffvmod.ec_ref_pcm && !pcm_is_ready(ffvmod.ec_ref_pcm)) {
+ ALOGE("%s: %s", __func__, pcm_get_error(ffvmod.ec_ref_pcm));
+ ret = -EIO;
+ goto exit;
+ }
+
+ if (pcm_prepare(ffvmod.ec_ref_pcm) < 0) {
+ ALOGE("%s: pcm prepare for ec ref loopback failed", __func__);
+ ret = -EINVAL;
+ }
+
+ ffvmod.capture_started = false;
+ pthread_mutex_unlock(&ffvmod.init_lock);
+ ALOGV("%s: exit", __func__);
+ return 0;
+
+exit:
+ if (ffvmod.ec_ref_pcm) {
+ pcm_close(ffvmod.ec_ref_pcm);
+ ffvmod.ec_ref_pcm = NULL;
+ }
+ list_remove(&uc_info_tx->list);
+ disable_snd_device(adev, in_snd_device);
+ disable_audio_route(adev, uc_info_tx);
+ free(uc_info_tx);
+ pthread_mutex_unlock(&ffvmod.init_lock);
+ return ret;
+}
+
+void audio_extn_ffv_append_ec_ref_dev_name(char *device_name)
+{
+ if (ffvmod.ec_ref_dev == AUDIO_DEVICE_OUT_AUX_LINE)
+ strlcat(device_name, " lineout", DEVICE_NAME_MAX_SIZE);
+ ALOGD("%s: ec ref dev name %s", __func__, device_name);
+}
+
+int audio_extn_ffv_deinit_ec_ref_loopback(struct audio_device *adev,
+ snd_device_t snd_device)
+{
+ struct audio_usecase *uc_info_tx = NULL;
+ snd_device_t in_snd_device;
+ int ret = 0;
+
+ ALOGV("%s: entry", __func__);
+ if (ffvmod.split_ec_ref_data) {
+ ALOGV("%s: Ignore ec ref loopback init", __func__);
+ return 0;
+ }
+
+ in_snd_device = platform_get_ec_ref_loopback_snd_device(ffvmod.ec_ref_ch_cnt);
+ uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_EC_REF_LOOPBACK);
+ pthread_mutex_lock(&ffvmod.init_lock);
+ if (ffvmod.ec_ref_pcm) {
+ pcm_close(ffvmod.ec_ref_pcm);
+ ffvmod.ec_ref_pcm = NULL;
+ }
+ disable_snd_device(adev, in_snd_device);
+ if (uc_info_tx) {
+ list_remove(&uc_info_tx->list);
+ disable_audio_route(adev, uc_info_tx);
+ free(uc_info_tx);
+ }
+ pthread_mutex_unlock(&ffvmod.init_lock);
+ ALOGV("%s: exit", __func__);
+ return ret;
+}
+
+int32_t audio_extn_ffv_read(struct audio_stream_in *stream,
+ void *buffer, size_t bytes)
+{
+ int status = 0;
+ int16_t *in_ptr = NULL, *process_in_ptr = NULL, *process_out_ptr = NULL;
+ int16_t *process_ec_ref_ptr = NULL;
+ size_t in_buf_size, out_buf_size, bytes_to_copy;
+ int retry_num = 0;
+ int i, j, ch;
+ int total_in_ch, in_ch, ec_ref_ch;
+
+ if (!ffvmod.ffv_lib_handle) {
+ ALOGE("%s: ffv_lib_handle not initialized", __func__);
+ return -EINVAL;
+ }
+
+ if (!ffvmod.handle) {
+ ALOGE("%s: ffv module handle not initialized", __func__);
+ return -EINVAL;
+ }
+
+ if (!ffvmod.in || !ffvmod.in->pcm) {
+ ALOGE("%s: capture session not initiliazed", __func__);
+ return -EINVAL;
+ }
+
+ if (!ffvmod.split_ec_ref_data && !ffvmod.ec_ref_pcm) {
+ ALOGE("%s: ec ref session not initiliazed", __func__);
+ return -EINVAL;
+ }
+
+ if (!ffvmod.capture_started) {
+ /* pcm_start of capture and ec ref session before read to reduce drift */
+ pcm_start(ffvmod.in->pcm);
+ while (status && (retry_num < FFV_PCM_MAX_RETRY)) {
+ usleep(FFV_PCM_SLEEP_WAIT);
+ retry_num++;
+ ALOGI("%s: pcm_start retrying..status %d errno %d, retry cnt %d",
+ __func__, status, errno, retry_num);
+ status = pcm_start(ffvmod.in->pcm);
+ }
+ if (status) {
+ ALOGE("%s: ERROR. pcm_start failed, returned status %d - %s",
+ __func__, status, pcm_get_error(ffvmod.in->pcm));
+ return status;
+ }
+ retry_num = 0;
+
+ if (!ffvmod.split_ec_ref_data) {
+ pcm_start(ffvmod.ec_ref_pcm);
+ while (status && (retry_num < FFV_PCM_MAX_RETRY)) {
+ usleep(FFV_PCM_SLEEP_WAIT);
+ retry_num++;
+ ALOGI("%s: pcm_start retrying..status %d errno %d, retry cnt %d",
+ __func__, status, errno, retry_num);
+ status = pcm_start(ffvmod.ec_ref_pcm);
+ }
+ if (status) {
+ ALOGE("%s: ERROR. pcm_start failed, returned status %d - %s",
+ __func__, status, pcm_get_error(ffvmod.ec_ref_pcm));
+ return status;
+ }
+ }
+ ffvmod.capture_started = true;
+ }
+
+ ALOGVV("%s: pcm_read reading bytes=%d", __func__, ffvmod.in_buf_size);
+ status = pcm_read(ffvmod.in->pcm, ffvmod.in_buf, ffvmod.in_buf_size);
+ if (status) {
+ ALOGE("%s: pcm read failed status %d - %s", __func__, status,
+ pcm_get_error(ffvmod.in->pcm));
+ goto exit;
+ }
+ ALOGVV("%s: pcm_read done", __func__);
+
+ if (!ffvmod.split_ec_ref_data) {
+ /* read EC ref data */
+ ALOGVV("%s: ec ref pcm_read reading bytes=%d", __func__, ffvmod.ec_ref_buf_size);
+ status = pcm_read(ffvmod.ec_ref_pcm, ffvmod.ec_ref_buf, ffvmod.ec_ref_buf_size);
+ if (status) {
+ ALOGE("%s: ec ref pcm read failed status %d - %s", __func__, status,
+ pcm_get_error(ffvmod.ec_ref_pcm));
+ goto exit;
+ }
+ ALOGVV("%s: ec ref pcm_read done", __func__);
+ process_in_ptr = (int16_t *)ffvmod.in_buf;
+ process_ec_ref_ptr = (int16_t *)ffvmod.ec_ref_buf;
+ in_buf_size = ffvmod.in_buf_size;
+ } else {
+ /* split input buffer into actual input channels and EC ref channels */
+ in_ptr = (int16_t *)ffvmod.in_buf;
+ process_in_ptr = (int16_t *)ffvmod.split_in_buf;
+ process_ec_ref_ptr = (int16_t *)ffvmod.ec_ref_buf;
+ total_in_ch = ffvmod.capture_config.channels;
+ ec_ref_ch = ffvmod.ec_ref_config.channels;
+ in_ch = total_in_ch - ec_ref_ch;
+ for (i = 0; i < ffvmod.capture_config.period_size; i++) {
+ for (ch = 0; ch < in_ch; ch++) {
+ process_in_ptr[i*in_ch+ch] =
+ in_ptr[i*total_in_ch+ch];
+ }
+ for (ch = 0; ch < ec_ref_ch; ch++) {
+ process_ec_ref_ptr[i*ec_ref_ch+ch] =
+ in_ptr[i*total_in_ch+in_ch+ch];
+ }
+ }
+ in_buf_size = ffvmod.split_in_buf_size;
+ }
+ process_out_ptr = (int16_t *)ffvmod.out_buf;
+
+ ffv_process_fn(ffvmod.handle, process_in_ptr,
+ process_out_ptr, process_ec_ref_ptr);
+ out_buf_size = ffvmod.out_buf_size;
+ bytes_to_copy = (bytes <= out_buf_size) ? bytes : out_buf_size;
+ memcpy(buffer, process_out_ptr, bytes_to_copy);
+ if (bytes_to_copy != out_buf_size)
+ ALOGD("%s: out buffer data dropped, copied %d bytes",
+ __func__, bytes_to_copy);
+
+#ifdef FFV_PCM_DUMP
+ if (ffvmod.fp_input)
+ fwrite(ffvmod.in_buf, 1, ffvmod.in_buf_size, ffvmod.fp_input);
+ if (ffvmod.fp_ecref)
+ fwrite(ffvmod.ec_ref_buf, 1, ffvmod.ec_ref_buf_size, ffvmod.fp_ecref);
+ if (ffvmod.fp_split_input)
+ fwrite(ffvmod.split_in_buf, 1, ffvmod.split_in_buf_size, ffvmod.fp_split_input);
+ if (ffvmod.fp_output)
+ fwrite(process_out_ptr, 1, bytes_to_copy, ffvmod.fp_output);
+#endif
+
+exit:
+ return status;
+}
+
+void audio_extn_ffv_set_parameters(struct audio_device *adev __unused,
+ struct str_parms *parms)
+{
+ int err;
+ int val;
+ int ret = 0;
+ char value[128];
+
+ /* FFV params are required to be set before start of recording */
+ if (!ffvmod.handle) {
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_FFV_MODE_ON, value,
+ sizeof(value));
+ if (ret >= 0) {
+ str_parms_del(parms, AUDIO_PARAMETER_FFV_MODE_ON);
+ if (strcmp(value, "true") == 0) {
+ ALOGD("%s: Setting FFV mode to true", __func__);
+ ffvmod.is_ffvmode_on = true;
+ } else {
+ ALOGD("%s: Resetting FFV mode to false", __func__);
+ ffvmod.is_ffvmode_on = false;
+ }
+ }
+
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_FFV_SPLIT_EC_REF_DATA, value,
+ sizeof(value));
+ if (ret >= 0) {
+ str_parms_del(parms, AUDIO_PARAMETER_FFV_SPLIT_EC_REF_DATA);
+ if (strcmp(value, "true") == 0) {
+ ALOGD("%s: ec ref is packed with mic captured data", __func__);
+ ffvmod.split_ec_ref_data = true;
+ } else {
+ ALOGD("%s: ec ref is captured separately", __func__);
+ ffvmod.split_ec_ref_data = false;
+ }
+ }
+ ret = str_parms_get_int(parms, AUDIO_PARAMETER_FFV_EC_REF_CHANNEL_COUNT, &val);
+ if (ret >= 0) {
+ str_parms_del(parms, AUDIO_PARAMETER_FFV_EC_REF_CHANNEL_COUNT);
+ if (val == 1) {
+ ALOGD("%s: mono ec ref", __func__);
+ ffvmod.ec_ref_ch_cnt = FFV_CHANNEL_MODE_MONO;
+ } else if (val == 2) {
+ ALOGD("%s: stereo ec ref", __func__);
+ ffvmod.ec_ref_ch_cnt = FFV_CHANNEL_MODE_STEREO;
+ } else {
+ ALOGE("%s: Invalid ec ref", __func__);
+ }
+ }
+
+ ret = str_parms_get_int(parms, AUDIO_PARAMETER_FFV_EC_REF_DEVICE, &val);
+ if (ret >= 0) {
+ str_parms_del(parms, AUDIO_PARAMETER_FFV_EC_REF_DEVICE);
+ if (val & AUDIO_DEVICE_OUT_SPEAKER) {
+ ALOGD("%s: capture ec ref from speaker", __func__);
+ ffvmod.ec_ref_dev = AUDIO_DEVICE_OUT_SPEAKER;
+ } else if (val & AUDIO_DEVICE_OUT_AUX_LINE) {
+ ALOGD("%s: capture ec ref from line out", __func__);
+ ffvmod.ec_ref_dev = AUDIO_DEVICE_OUT_AUX_LINE;
+ } else {
+ ALOGE("%s: Invalid ec ref out device", __func__);
+ }
+ }
+
+ ret = str_parms_get_int(parms, AUDIO_PARAMETER_FFV_CHANNEL_INDEX, &val);
+ if (ret >= 0) {
+ str_parms_del(parms, AUDIO_PARAMETER_FFV_CHANNEL_INDEX);
+ ALOGD("%s: set target chan index %d", __func__, val);
+ ffvmod.target_ch_idx = val;
+ }
+ }
+}