| /* |
| * Copyright (c) 2017-2018, 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 "audio_extn.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" |
| |
| #if LINUX_ENABLED |
| #define FFV_CONFIG_FILE_PATH "/etc/BF_1out.cfg" |
| #ifdef __LP64__ |
| #define FFV_LIB "/usr/lib64/libffv.so" |
| #else |
| #define FFV_LIB "/usr/lib/libffv.so" |
| #endif |
| #else |
| #define FFV_CONFIG_FILE_PATH "/vendor/etc/BF_1out.cfg" |
| #ifdef __LP64__ |
| #define FFV_LIB "/vendor/lib64/libffv.so" |
| #else |
| #define FFV_LIB "/vendor/lib/libffv.so" |
| #endif |
| #endif |
| |
| #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_QUAD 6 |
| #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, |
| int product_id, const char* prduct_license); |
| 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.vendor.audio.sdk.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 ffv_key, char* ffv_lic) |
| { |
| int ret = -EINVAL; |
| |
| if (audio_extn_ffv_check_usecase(in)) { |
| if (!audio_extn_ffv_stream_init(in, ffv_key, ffv_lic)) { |
| 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 __unused) |
| { |
| 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, int key, char* lic) |
| { |
| 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, (char *)sm_buffer, 0, |
| &total_mem_size, key, lic); |
| 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 RUN_KEEP_ALIVE_IN_ARM_FFV |
| audio_extn_keep_alive_start(KEEP_ALIVE_OUT_PRIMARY); |
| #endif |
| #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(); |
| #ifdef RUN_KEEP_ALIVE_IN_ARM_FFV |
| audio_extn_keep_alive_stop(KEEP_ALIVE_OUT_PRIMARY); |
| #endif |
| ffvmod.handle = NULL; |
| 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 if (ffvmod.capture_config.channels == FFV_CHANNEL_MODE_QUAD) { |
| return SND_DEVICE_IN_HANDSET_QMIC; |
| } 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 __unused) |
| { |
| 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; |
| } |
| |
| ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d format %d", |
| __func__, adev->snd_card, ffvmod.ec_ref_pcm_id, ffvmod.ec_ref_config.channels, |
| ffvmod.ec_ref_config.format); |
| 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; |
| } |
| |
| ALOGV("%s: pcm_prepare", __func__); |
| 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_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 __unused) |
| { |
| 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 __unused, |
| 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, 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 < (int)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 %zu 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 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 = -1; |
| if (str_parms_get_int(parms, AUDIO_PARAMETER_FFV_EC_REF_DEVICE, &val) >= 0) { |
| ret = 1; |
| str_parms_del(parms, AUDIO_PARAMETER_FFV_EC_REF_DEVICE); |
| } else if (str_parms_get_int(parms, AUDIO_PARAMETER_DEVICE_CONNECT, &val) >= 0) { |
| ret = 1; |
| str_parms_del(parms, AUDIO_PARAMETER_DEVICE_CONNECT); |
| } |
| if (ret == 1) { |
| 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_LINE) { |
| ALOGD("%s: capture ec ref from line out", __func__); |
| ffvmod.ec_ref_dev = AUDIO_DEVICE_OUT_LINE; |
| } |
| } |
| |
| ret = str_parms_get_int(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, &val); |
| if (ret >= 0) { |
| str_parms_del(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT); |
| if (val & AUDIO_DEVICE_OUT_LINE) { |
| ALOGD("%s: capture ec ref from speaker", __func__); |
| ffvmod.ec_ref_dev = AUDIO_DEVICE_OUT_SPEAKER; |
| } |
| } |
| |
| 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; |
| } |
| } |
| } |