blob: 48de4b1a230c05c7cf8223f14ae64cef374ecfb2 [file] [log] [blame]
/*
* Copyright (c) 2019-2021, 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.
*
* Changes from Qualcomm Innovation Center are provided under the following license:
*
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#define ATRACE_TAG (ATRACE_TAG_AUDIO | ATRACE_TAG_HAL)
#define LOG_TAG "PAL: SoundTriggerEngineCapi"
#include "SoundTriggerEngineCapi.h"
#include <cutils/trace.h>
#include <dlfcn.h>
#include "StreamSoundTrigger.h"
#include "Stream.h"
#include "SoundTriggerPlatformInfo.h"
#include "VoiceUIInterface.h"
#define CNN_BUFFER_LENGTH 10000
#define CNN_FRAME_SIZE 320
ST_DBG_DECLARE(static int keyword_detection_cnt = 0);
ST_DBG_DECLARE(static int user_verification_cnt = 0);
void SoundTriggerEngineCapi::BufferThreadLoop(
SoundTriggerEngineCapi *capi_engine)
{
StreamSoundTrigger *s = nullptr;
int32_t status = 0;
int32_t detection_state = ENGINE_IDLE;
PAL_DBG(LOG_TAG, "Enter");
if (!capi_engine) {
PAL_ERR(LOG_TAG, "Invalid sound trigger capi engine");
return;
}
std::unique_lock<std::mutex> lck(capi_engine->event_mutex_);
while (!capi_engine->exit_thread_) {
PAL_VERBOSE(LOG_TAG, "waiting on cond, processing started = %d",
capi_engine->processing_started_);
// Wait for keyword buffer data from DSP
if (!capi_engine->processing_started_)
capi_engine->cv_.wait(lck);
PAL_VERBOSE(LOG_TAG, "done waiting on cond, exit buffering = %d",
capi_engine->exit_buffering_);
if (capi_engine->exit_thread_) {
break;
}
/*
* If 1st stage buffering overflows before 2nd stage starts processing,
* the below functions need to be called to reset the 1st stage session
* for the next detection. We might be able to check states of the engine
* to avoid this buffering flag.
*/
if (capi_engine->exit_buffering_) {
continue; // skip over processing if we want to exit already
}
if (capi_engine->processing_started_) {
s = dynamic_cast<StreamSoundTrigger *>(capi_engine->stream_handle_);
capi_engine->bytes_processed_ = 0;
if (capi_engine->detection_type_ ==
ST_SM_TYPE_KEYWORD_DETECTION) {
status = capi_engine->StartKeywordDetection();
/*
* StreamSoundTrigger may call stop recognition to second stage
* engines when one of the second stage engine reject detection.
* So check processing_started_ before notify stream in case
* stream has already stopped recognition.
*/
if (capi_engine->processing_started_) {
if (status)
detection_state = KEYWORD_DETECTION_REJECT;
else
detection_state = capi_engine->detection_state_;
lck.unlock();
s->SetEngineDetectionState(detection_state);
lck.lock();
}
} else if (capi_engine->detection_type_ ==
ST_SM_TYPE_USER_VERIFICATION) {
status = capi_engine->StartUserVerification();
/*
* StreamSoundTrigger may call stop recognition to second stage
* engines when one of the second stage engine reject detection.
* So check processing_started_ before notify stream in case
* stream has already stopped recognition.
*/
if (capi_engine->processing_started_) {
if (status)
detection_state = USER_VERIFICATION_REJECT;
else
detection_state = capi_engine->detection_state_;
lck.unlock();
s->SetEngineDetectionState(detection_state);
lck.lock();
}
}
capi_engine->detection_state_ = ENGINE_IDLE;
capi_engine->keyword_detected_ = false;
capi_engine->processing_started_ = false;
}
}
PAL_DBG(LOG_TAG, "Exit");
}
int32_t SoundTriggerEngineCapi::StartKeywordDetection()
{
int32_t status = 0;
char *process_input_buff = nullptr;
capi_v2_err_t rc = CAPI_V2_EOK;
capi_v2_stream_data_t *stream_input = nullptr;
sva_result_t *result_cfg_ptr = nullptr;
int32_t read_size = 0;
size_t start_idx = 0;
size_t end_idx = 0;
capi_v2_buf_t capi_result;
bool buffer_advanced = false;
size_t lab_buffer_size = 0;
bool first_buffer_processed = false;
FILE *keyword_detection_fd = nullptr;
ChronoSteadyClock_t process_start;
ChronoSteadyClock_t process_end;
ChronoSteadyClock_t capi_call_start;
ChronoSteadyClock_t capi_call_end;
uint64_t process_duration = 0;
uint64_t total_capi_process_duration = 0;
uint64_t total_capi_get_param_duration = 0;
PAL_DBG(LOG_TAG, "Enter");
if (!reader_) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "Invalid ring buffer reader");
goto exit;
}
reader_->getIndices(&buffer_start_, &buffer_end_);
if (buffer_start_ >= buffer_end_) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "Invalid keyword indices");
goto exit;
}
// calculate start and end index including tolerance
if (buffer_start_ > UsToBytes(kw_start_tolerance_)) {
buffer_start_ -= UsToBytes(kw_start_tolerance_);
} else {
buffer_start_ = 0;
}
lab_buffer_size = buffer_size_;
buffer_size_ = buffer_end_ - buffer_start_;
/*
* As per requirement in PDK, input buffer size for
* second stage should be in multiple of 10 ms(10000us).
*/
buffer_size_ -= buffer_size_ % (UsToBytes(10000));
buffer_end_ += UsToBytes(kw_end_tolerance_ + data_after_kw_end_);
PAL_DBG(LOG_TAG, "buffer_start_: %u, buffer_end_: %u",
buffer_start_, buffer_end_);
if (vui_ptfm_info_->GetEnableDebugDumps()) {
ST_DBG_FILE_OPEN_WR(keyword_detection_fd, ST_DEBUG_DUMP_LOCATION,
"keyword_detection", "bin", keyword_detection_cnt);
PAL_DBG(LOG_TAG, "keyword detection data stored in: keyword_detection_%d.bin",
keyword_detection_cnt);
keyword_detection_cnt++;
}
memset(&capi_result, 0, sizeof(capi_result));
process_input_buff = (char*)calloc(1, buffer_size_);
if (!process_input_buff) {
status = -ENOMEM;
PAL_ERR(LOG_TAG, "failed to allocate process input buff, status %d",
status);
goto exit;
}
stream_input = (capi_v2_stream_data_t *)
calloc(1, sizeof(capi_v2_stream_data_t));
if (!stream_input) {
status = -ENOMEM;
PAL_ERR(LOG_TAG, "failed to allocate stream input, status %d", status);
goto exit;
}
stream_input->buf_ptr = (capi_v2_buf_t*)calloc(1, sizeof(capi_v2_buf_t));
if (!stream_input->buf_ptr) {
status = -ENOMEM;
PAL_ERR(LOG_TAG, "failed to allocate stream_input->buf_ptr, status %d",
status);
goto exit;
}
result_cfg_ptr = (sva_result_t*)calloc(1, sizeof(sva_result_t));
if (!result_cfg_ptr) {
status = -ENOMEM;
PAL_ERR(LOG_TAG, "failed to allocate result cfg ptr status %d", status);
goto exit;
}
process_start = std::chrono::steady_clock::now();
while (!exit_buffering_ &&
(bytes_processed_ < buffer_end_ - buffer_start_)) {
/* Original code had some time of wait will need to revisit*/
/* need to take into consideration the start and end buffer*/
if (!reader_->isEnabled()) {
status = -EINVAL;
goto exit;
}
/* advance the offset to ensure we are reading at the right place */
if (!buffer_advanced && buffer_start_ > 0) {
if (reader_->advanceReadOffset(buffer_start_)) {
buffer_advanced = true;
} else {
continue;
}
}
if (!reader_->waitForBuffers(buffer_size_))
continue;
read_size = reader_->read((void*)process_input_buff, buffer_size_);
if (read_size == 0) {
continue;
} else if (read_size < 0) {
status = read_size;
PAL_ERR(LOG_TAG, "Failed to read from buffer, status %d", status);
goto exit;
}
PAL_INFO(LOG_TAG, "Processed: %u, start: %u, end: %u",
bytes_processed_, buffer_start_, buffer_end_);
stream_input->bufs_num = 1;
stream_input->buf_ptr->max_data_len = buffer_size_;
stream_input->buf_ptr->actual_data_len = read_size;
stream_input->buf_ptr->data_ptr = (int8_t *)process_input_buff;
if (vui_ptfm_info_->GetEnableDebugDumps()) {
ST_DBG_FILE_WRITE(keyword_detection_fd,
process_input_buff, read_size);
}
PAL_VERBOSE(LOG_TAG, "Calling Capi Process");
capi_call_start = std::chrono::steady_clock::now();
ATRACE_BEGIN("Second stage KW process");
rc = capi_handle_->vtbl_ptr->process(capi_handle_,
&stream_input, nullptr);
ATRACE_END();
capi_call_end = std::chrono::steady_clock::now();
total_capi_process_duration +=
std::chrono::duration_cast<std::chrono::milliseconds>(
capi_call_end - capi_call_start).count();
if (CAPI_V2_EFAILED == rc) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "capi process failed, status %d", status);
goto exit;
}
bytes_processed_ += read_size;
capi_result.data_ptr = (int8_t*)result_cfg_ptr;
capi_result.actual_data_len = sizeof(sva_result_t);
capi_result.max_data_len = sizeof(sva_result_t);
PAL_VERBOSE(LOG_TAG, "Calling Capi get param for status");
capi_call_start = std::chrono::steady_clock::now();
rc = capi_handle_->vtbl_ptr->get_param(capi_handle_,
SVA_ID_RESULT, nullptr, &capi_result);
capi_call_end = std::chrono::steady_clock::now();
total_capi_get_param_duration +=
std::chrono::duration_cast<std::chrono::milliseconds>(
capi_call_end - capi_call_start).count();
if (CAPI_V2_EFAILED == rc) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "capi get param failed, status %d", status);
goto exit;
}
det_conf_score_ = result_cfg_ptr->best_confidence;
if (result_cfg_ptr->is_detected) {
exit_buffering_ = true;
detection_state_ = KEYWORD_DETECTION_SUCCESS;
__builtin_add_overflow(result_cfg_ptr->start_position * CNN_FRAME_SIZE,
buffer_start_, &start_idx);
__builtin_add_overflow(result_cfg_ptr->end_position * CNN_FRAME_SIZE,
buffer_start_, &end_idx);
vui_intf_->SetSecondStageDetLevels(stream_handle_,
engine_type_, det_conf_score_);
PAL_INFO(LOG_TAG, "KW Second Stage Detected, start index %zu, end index %zu",
start_idx, end_idx);
} else if (bytes_processed_ >= buffer_end_ - buffer_start_) {
detection_state_ = KEYWORD_DETECTION_REJECT;
vui_intf_->SetSecondStageDetLevels(stream_handle_,
engine_type_, det_conf_score_);
PAL_INFO(LOG_TAG, "KW Second Stage rejected");
}
PAL_INFO(LOG_TAG, "KW second stage conf level %d", det_conf_score_);
if (!first_buffer_processed) {
buffer_size_ = lab_buffer_size;
first_buffer_processed = true;
}
}
exit:
PAL_INFO(LOG_TAG, "Issuing capi_set_param for param %d",
SVA_ID_REINIT_ALL);
rc = capi_handle_->vtbl_ptr->set_param(capi_handle_,
SVA_ID_REINIT_ALL, nullptr, nullptr);
if (CAPI_V2_EOK != rc) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "set param SVA_ID_REINIT_ALL failed, status = %d",
rc);
}
process_end = std::chrono::steady_clock::now();
process_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
process_end - process_start).count();
PAL_INFO(LOG_TAG, "KW processing time: Bytes processed %u, Total processing "
"time %llums, Algo process time %llums, get result time %llums",
bytes_processed_, (long long)process_duration,
(long long)total_capi_process_duration,
(long long)total_capi_get_param_duration);
if (vui_ptfm_info_->GetEnableDebugDumps()) {
ST_DBG_FILE_CLOSE(keyword_detection_fd);
}
if (reader_)
reader_->updateState(READER_DISABLED);
if (process_input_buff)
free(process_input_buff);
if (stream_input) {
if (stream_input->buf_ptr)
free(stream_input->buf_ptr);
free(stream_input);
}
if (result_cfg_ptr)
free(result_cfg_ptr);
PAL_DBG(LOG_TAG, "Exit, status %d", status);
return status;
}
int32_t SoundTriggerEngineCapi::StartUserVerification()
{
int32_t status = 0;
char *process_input_buff = nullptr;
capi_v2_err_t rc = CAPI_V2_EOK;
capi_v2_stream_data_t *stream_input = nullptr;
capi_v2_buf_t capi_uv_ptr;
stage2_uv_wrapper_result *result_cfg_ptr = nullptr;
stage2_uv_wrapper_stage1_uv_score_t *uv_cfg_ptr = nullptr;
int32_t read_size = 0;
capi_v2_buf_t capi_result;
bool buffer_advanced = false;
StreamSoundTrigger *str = nullptr;
struct detection_event_info *info = nullptr;
FILE *user_verification_fd = nullptr;
ChronoSteadyClock_t process_start;
ChronoSteadyClock_t process_end;
ChronoSteadyClock_t capi_call_start;
ChronoSteadyClock_t capi_call_end;
uint64_t process_duration = 0;
uint64_t total_capi_process_duration = 0;
uint64_t total_capi_get_param_duration = 0;
PAL_DBG(LOG_TAG, "Enter");
if (!reader_) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "Invalid ring buffer reader");
goto exit;
}
reader_->getIndices(&buffer_start_, &buffer_end_);
if (buffer_start_ >= buffer_end_) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "Invalid keyword indices");
goto exit;
}
// calculate start and end index including tolerance
if (buffer_start_ > UsToBytes(data_before_kw_start_)) {
buffer_start_ -= UsToBytes(data_before_kw_start_);
} else {
buffer_start_ = 0;
}
buffer_end_ += UsToBytes(kw_end_tolerance_);
buffer_size_ = buffer_end_ - buffer_start_;
if (vui_ptfm_info_->GetEnableDebugDumps()) {
ST_DBG_FILE_OPEN_WR(user_verification_fd, ST_DEBUG_DUMP_LOCATION,
"user_verification", "bin", user_verification_cnt);
PAL_DBG(LOG_TAG, "User Verification data stored in: user_verification_%d.bin",
user_verification_cnt);
user_verification_cnt++;
}
memset(&capi_uv_ptr, 0, sizeof(capi_uv_ptr));
memset(&capi_result, 0, sizeof(capi_result));
process_input_buff = (char*)calloc(1, buffer_size_);
if (!process_input_buff) {
PAL_ERR(LOG_TAG, "failed to allocate process input buff");
status = -ENOMEM;
goto exit;
}
stream_input = (capi_v2_stream_data_t *)
calloc(1, sizeof(capi_v2_stream_data_t));
if (!stream_input) {
PAL_ERR(LOG_TAG, "failed to allocate stream input");
status = -ENOMEM;
goto exit;
}
stream_input->buf_ptr = (capi_v2_buf_t*)calloc(1, sizeof(capi_v2_buf_t));
if (!stream_input->buf_ptr) {
PAL_ERR(LOG_TAG, "failed to allocate buf ptr");
status = -ENOMEM;
goto exit;
}
result_cfg_ptr = (stage2_uv_wrapper_result*)
calloc(1, sizeof(stage2_uv_wrapper_result));
if (!result_cfg_ptr) {
PAL_ERR(LOG_TAG, "failed to allocate result cfg ptr");
status = -ENOMEM;
goto exit;
}
uv_cfg_ptr = (stage2_uv_wrapper_stage1_uv_score_t *)
calloc(1, sizeof(stage2_uv_wrapper_stage1_uv_score_t));
if (!uv_cfg_ptr) {
PAL_ERR(LOG_TAG, "failed to allocate uv cfg ptr");
status = -ENOMEM;
goto exit;
}
str = dynamic_cast<StreamSoundTrigger *>(stream_handle_);
if (vui_intf_->GetModuleType(stream_handle_) == ST_MODULE_TYPE_GMM) {
info = (struct detection_event_info *)vui_intf_->GetDetectionEventInfo();
if (!info) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "Failed to get detection event info");
goto exit;
}
confidence_score_ = info->confidence_levels[1];
uv_cfg_ptr->stage1_uv_score = confidence_score_;
capi_uv_ptr.data_ptr = (int8_t *)uv_cfg_ptr;
capi_uv_ptr.actual_data_len = sizeof(stage2_uv_wrapper_stage1_uv_score_t);
capi_uv_ptr.max_data_len = sizeof(stage2_uv_wrapper_stage1_uv_score_t);
PAL_VERBOSE(LOG_TAG, "Issuing capi_set_param for param %d",
STAGE2_UV_WRAPPER_ID_SVA_UV_SCORE);
rc = capi_handle_->vtbl_ptr->set_param(capi_handle_,
STAGE2_UV_WRAPPER_ID_SVA_UV_SCORE, nullptr, &capi_uv_ptr);
if (CAPI_V2_EOK != rc) {
PAL_ERR(LOG_TAG, "set param STAGE2_UV_WRAPPER_ID_SVA_UV_SCORE failed with %d",
rc);
status = -EINVAL;
goto exit;
}
}
if (kw_end_timestamp_ > 0)
buffer_end_ = UsToBytes(kw_end_timestamp_);
if (kw_start_timestamp_ > 0)
buffer_start_ = UsToBytes(kw_start_timestamp_);
process_start = std::chrono::steady_clock::now();
while (!exit_buffering_ &&
(bytes_processed_ < buffer_end_ - buffer_start_)) {
/* Original code had some time of wait will need to revisit*/
/* need to take into consideration the start and end buffer*/
if (!reader_->isEnabled()) {
status = -EINVAL;
goto exit;
}
/* advance the offset to ensure we are reading at the right place */
if (!buffer_advanced && buffer_start_ > 0) {
if (reader_->advanceReadOffset(buffer_start_)) {
buffer_advanced = true;
} else {
continue;
}
}
if (!reader_->waitForBuffers(buffer_size_))
continue;
read_size = reader_->read((void*)process_input_buff, buffer_size_);
if (read_size == 0) {
continue;
} else if (read_size < 0) {
status = read_size;
PAL_ERR(LOG_TAG, "Failed to read from buffer, status %d", status);
goto exit;
}
PAL_INFO(LOG_TAG, "Processed: %u, start: %u, end: %u",
bytes_processed_, buffer_start_, buffer_end_);
stream_input->bufs_num = 1;
stream_input->buf_ptr->max_data_len = buffer_size_;
stream_input->buf_ptr->actual_data_len = read_size;
stream_input->buf_ptr->data_ptr = (int8_t *)process_input_buff;
if (vui_ptfm_info_->GetEnableDebugDumps()) {
ST_DBG_FILE_WRITE(user_verification_fd,
process_input_buff, read_size);
}
PAL_VERBOSE(LOG_TAG, "Calling Capi Process\n");
capi_call_start = std::chrono::steady_clock::now();
ATRACE_BEGIN("Second stage uv process");
rc = capi_handle_->vtbl_ptr->process(capi_handle_,
&stream_input, nullptr);
ATRACE_END();
capi_call_end = std::chrono::steady_clock::now();
total_capi_process_duration +=
std::chrono::duration_cast<std::chrono::milliseconds>(
capi_call_end - capi_call_start).count();
if (CAPI_V2_EFAILED == rc) {
PAL_ERR(LOG_TAG, "capi process failed\n");
status = -EINVAL;
goto exit;
}
bytes_processed_ += read_size;
capi_result.data_ptr = (int8_t*)result_cfg_ptr;
capi_result.actual_data_len = sizeof(stage2_uv_wrapper_result);
capi_result.max_data_len = sizeof(stage2_uv_wrapper_result);
PAL_VERBOSE(LOG_TAG, "Calling Capi get param for result\n");
capi_call_start = std::chrono::steady_clock::now();
ATRACE_BEGIN("Second stage uv get result");
rc = capi_handle_->vtbl_ptr->get_param(capi_handle_,
STAGE2_UV_WRAPPER_ID_RESULT, nullptr, &capi_result);
ATRACE_END();
capi_call_end = std::chrono::steady_clock::now();
total_capi_get_param_duration +=
std::chrono::duration_cast<std::chrono::milliseconds>(
capi_call_end - capi_call_start).count();
if (CAPI_V2_EFAILED == rc) {
PAL_ERR(LOG_TAG, "capi get param failed\n");
status = -EINVAL;
goto exit;
}
det_conf_score_ = (int32_t)result_cfg_ptr->final_user_score;
if (result_cfg_ptr->is_detected) {
exit_buffering_ = true;
detection_state_ = USER_VERIFICATION_SUCCESS;
vui_intf_->SetSecondStageDetLevels(stream_handle_,
engine_type_, det_conf_score_);
PAL_INFO(LOG_TAG, "UV Second Stage Detected");
} else if (bytes_processed_ >= buffer_end_ - buffer_start_) {
detection_state_ = USER_VERIFICATION_REJECT;
vui_intf_->SetSecondStageDetLevels(stream_handle_,
engine_type_, det_conf_score_);
PAL_INFO(LOG_TAG, "UV Second Stage Rejected");
}
PAL_INFO(LOG_TAG, "UV second stage conf level %d", det_conf_score_);
}
exit:
process_end = std::chrono::steady_clock::now();
process_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
process_end - process_start).count();
PAL_INFO(LOG_TAG, "UV processing time: Bytes processed %u, Total processing "
"time %llums, Algo process time %llums, get result time %llums",
bytes_processed_, (long long)process_duration,
(long long)total_capi_process_duration,
(long long)total_capi_get_param_duration);
if (vui_ptfm_info_->GetEnableDebugDumps()) {
ST_DBG_FILE_CLOSE(user_verification_fd);
}
/* Reinit the UV module */
PAL_DBG(LOG_TAG, "%s: Issuing capi_set_param for param %d", __func__,
STAGE2_UV_WRAPPER_ID_REINIT);
rc = capi_handle_->vtbl_ptr->set_param(capi_handle_,
STAGE2_UV_WRAPPER_ID_REINIT, nullptr, nullptr);
if (CAPI_V2_EOK != rc) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "set_param STAGE2_UV_WRAPPER_ID_REINIT failed, status = %d",
status);
}
if (reader_)
reader_->updateState(READER_DISABLED);
if (process_input_buff)
free(process_input_buff);
if (stream_input) {
if (stream_input->buf_ptr)
free(stream_input->buf_ptr);
free(stream_input);
}
if (result_cfg_ptr)
free(result_cfg_ptr);
if (uv_cfg_ptr)
free(uv_cfg_ptr);
PAL_DBG(LOG_TAG, "Exit, status %d", status);
return status;
}
SoundTriggerEngineCapi::SoundTriggerEngineCapi(
Stream *s,
listen_model_indicator_enum type,
std::shared_ptr<VUIStreamConfig> sm_cfg)
{
int32_t status = 0;
PAL_DBG(LOG_TAG, "Enter");
engine_type_ = type;
sm_cfg_ = sm_cfg;
vui_intf_ = nullptr;
processing_started_ = false;
sm_data_ = nullptr;
exit_thread_ = false;
exit_buffering_ = false;
kw_start_timestamp_ = 0;
kw_end_timestamp_ = 0;
buffer_start_ = 0;
buffer_end_ = 0;
bytes_processed_ = 0;
reader_ = nullptr;
buffer_ = nullptr;
stream_handle_ = s;
confidence_threshold_ = 0;
detection_state_ = ENGINE_IDLE;
capi_handle_ = nullptr;
capi_lib_handle_ = nullptr;
capi_init_ = nullptr;
confidence_score_ = 0;
keyword_detected_ = false;
det_conf_score_ = 0;
memset(&in_model_buffer_param_, 0, sizeof(in_model_buffer_param_));
memset(&scratch_param_, 0, sizeof(scratch_param_));
vui_ptfm_info_ = VoiceUIPlatformInfo::GetInstance();
if (!vui_ptfm_info_) {
PAL_ERR(LOG_TAG, "No voice UI platform info present");
throw std::runtime_error("No voice UI platform info present");
}
kw_start_tolerance_ = sm_cfg_->GetKwStartTolerance();
kw_end_tolerance_ = sm_cfg_->GetKwEndTolerance();
data_before_kw_start_ = sm_cfg_->GetDataBeforeKwStart();
data_after_kw_end_ = sm_cfg_->GetDataAfterKwEnd();
ss_cfg_ = sm_cfg_->GetVUISecondStageConfig(engine_type_);
if (!ss_cfg_) {
PAL_ERR(LOG_TAG, "Failed to get second stage config");
throw std::runtime_error("Failed to get second stage config");
}
sample_rate_ = ss_cfg_->GetSampleRate();
bit_width_ = ss_cfg_->GetBitWidth();
channels_ = ss_cfg_->GetChannels();
detection_type_ = ss_cfg_->GetDetectionType();
lib_name_ = ss_cfg_->GetLibName();
buffer_size_ = UsToBytes(CNN_BUFFER_LENGTH);
// TODO: ST_SM_TYPE_CUSTOM_DETECTION
if (detection_type_ == ST_SM_TYPE_KEYWORD_DETECTION) {
capi_handle_ = (capi_v2_t *)calloc(1,
sizeof(capi_v2_t) + sizeof(char *));
} else if (detection_type_ == ST_SM_TYPE_USER_VERIFICATION) {
capi_handle_ = (capi_v2_t *)calloc(1,
sizeof(capi_v2_t) + (2 * sizeof(char *)));
}
if (!capi_handle_) {
status = -ENOMEM;
PAL_ERR(LOG_TAG, "failed to allocate capi handle = %d", status);
/* handle here */
goto err_exit;
}
capi_lib_handle_ = dlopen(lib_name_.c_str(), RTLD_NOW);
if (!capi_lib_handle_) {
status = -ENOMEM;
PAL_ERR(LOG_TAG, "failed to open capi so = %d", status);
/* handle here */
goto err_exit;
}
dlerror();
capi_init_ = (capi_v2_init_f)dlsym(capi_lib_handle_, "capi_v2_init");
if (!capi_init_) {
PAL_ERR(LOG_TAG, "failed to map capi init function = %d", status);
/* handle here */
goto err_exit;
}
return;
err_exit:
if (capi_handle_) {
free(capi_handle_);
capi_handle_ = nullptr;
}
if (capi_lib_handle_) {
dlclose(capi_lib_handle_);
capi_lib_handle_ = nullptr;
}
PAL_ERR(LOG_TAG, "constructor exit status = %d", status);
}
SoundTriggerEngineCapi::~SoundTriggerEngineCapi()
{
PAL_DBG(LOG_TAG, "Enter");
/*
* join thread if it is not joined, sometimes
* stop/unload may fail before deconstruction.
*/
if (buffer_thread_handler_.joinable()) {
processing_started_ = false;
std::unique_lock<std::mutex> lck(event_mutex_);
exit_thread_ = true;
exit_buffering_ = true;
cv_.notify_one();
lck.unlock();
buffer_thread_handler_.join();
PAL_INFO(LOG_TAG, "Thread joined");
}
if (buffer_) {
delete buffer_;
}
if (reader_) {
delete reader_;
}
if (capi_lib_handle_) {
dlclose(capi_lib_handle_);
capi_lib_handle_ = nullptr;
}
if (capi_handle_) {
capi_handle_->vtbl_ptr = nullptr;
free(capi_handle_);
capi_handle_ = nullptr;
}
PAL_DBG(LOG_TAG, "Exit");
}
int32_t SoundTriggerEngineCapi::StartSoundEngine()
{
int32_t status = 0;
processing_started_ = false;
exit_thread_ = false;
exit_buffering_ = false;
capi_v2_err_t rc = CAPI_V2_EOK;
capi_v2_buf_t capi_buf;
PAL_DBG(LOG_TAG, "Enter");
if (detection_type_ == ST_SM_TYPE_KEYWORD_DETECTION) {
sva_threshold_config_t *threshold_cfg = nullptr;
threshold_cfg = (sva_threshold_config_t*)
calloc(1, sizeof(sva_threshold_config_t));
if (!threshold_cfg) {
status = -ENOMEM;
PAL_ERR(LOG_TAG, "threshold cfg calloc failed, status %d", status);
return status;
}
capi_buf.data_ptr = (int8_t*) threshold_cfg;
capi_buf.actual_data_len = sizeof(sva_threshold_config_t);
capi_buf.max_data_len = sizeof(sva_threshold_config_t);
threshold_cfg->smm_threshold = confidence_threshold_;
PAL_DBG(LOG_TAG, "Keyword detection (CNN) confidence level = %d",
threshold_cfg->smm_threshold);
status = capi_handle_->vtbl_ptr->set_param(capi_handle_,
SVA_ID_THRESHOLD_CONFIG, nullptr, &capi_buf);
if (CAPI_V2_EOK != status) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "set param SVA_ID_THRESHOLD_CONFIG failed with %d",
status);
if (threshold_cfg)
free(threshold_cfg);
return status;
}
PAL_VERBOSE(LOG_TAG, "Issuing capi_set_param for param %d",
SVA_ID_REINIT_ALL);
status = capi_handle_->vtbl_ptr->set_param(capi_handle_,
SVA_ID_REINIT_ALL, nullptr, nullptr);
if (CAPI_V2_EOK != status) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "set param SVA_ID_REINIT_ALL failed, status = %d",
status);
if (threshold_cfg)
free(threshold_cfg);
return status;
}
detection_state_ = KEYWORD_DETECTION_PENDING;
} else if (detection_type_ == ST_SM_TYPE_USER_VERIFICATION) {
stage2_uv_wrapper_threshold_config_t *threshold_cfg = nullptr;
threshold_cfg = (stage2_uv_wrapper_threshold_config_t *)
calloc(1, sizeof(stage2_uv_wrapper_threshold_config_t));
if (!threshold_cfg) {
PAL_ERR(LOG_TAG, "failed to allocate threshold cfg");
status = -ENOMEM;
return status;
}
capi_buf.data_ptr = (int8_t *)threshold_cfg;
capi_buf.actual_data_len = sizeof(stage2_uv_wrapper_threshold_config_t);
capi_buf.max_data_len = sizeof(stage2_uv_wrapper_threshold_config_t);
threshold_cfg->threshold = confidence_threshold_;
threshold_cfg->anti_spoofing_enabled = 0;
threshold_cfg->anti_spoofing_threshold = 0;
PAL_DBG(LOG_TAG, "Keyword detection (UV) confidence level = %d",
threshold_cfg->threshold);
rc = capi_handle_->vtbl_ptr->set_param(capi_handle_,
STAGE2_UV_WRAPPER_ID_THRESHOLD, nullptr, &capi_buf);
if (CAPI_V2_EOK != rc) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "set param %d failed with %d",
STAGE2_UV_WRAPPER_ID_THRESHOLD, rc);
if (threshold_cfg)
free(threshold_cfg);
return status;
}
detection_state_ = USER_VERIFICATION_PENDING;
}
PAL_DBG(LOG_TAG, "Exit, status %d", status);
return status;
}
int32_t SoundTriggerEngineCapi::StopSoundEngine()
{
int32_t status = 0;
PAL_DBG(LOG_TAG, "Enter");
{
processing_started_ = false;
std::lock_guard<std::mutex> lck(event_mutex_);
exit_thread_ = true;
exit_buffering_ = true;
cv_.notify_one();
}
if (buffer_thread_handler_.joinable()) {
PAL_DBG(LOG_TAG, "Thread joined");
buffer_thread_handler_.join();
}
PAL_DBG(LOG_TAG, "Exit, status %d", status);
return status;
}
int32_t SoundTriggerEngineCapi::LoadSoundModel(Stream *s __unused,
uint8_t *data, uint32_t data_size)
{
int32_t status = 0;
capi_v2_err_t rc = CAPI_V2_EOK;
capi_v2_proplist_t init_set_proplist;
capi_v2_prop_t sm_prop_ptr;
capi_v2_buf_t capi_uv_ptr;
PAL_DBG(LOG_TAG, "Enter");
std::lock_guard<std::mutex> lck(mutex_);
if (!data) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "Invalid sound model data, status %d", status);
goto exit;
}
if (!capi_handle_) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "Capi handle not allocated, status %d", status);
goto exit;
}
sm_data_ = data;
sm_data_size_ = data_size;
sm_prop_ptr.id = CAPI_V2_CUSTOM_INIT_DATA;
sm_prop_ptr.payload.data_ptr = (int8_t *)sm_data_;
sm_prop_ptr.payload.actual_data_len = sm_data_size_;
sm_prop_ptr.payload.max_data_len = sm_data_size_;
init_set_proplist.props_num = 1;
init_set_proplist.prop_ptr = &sm_prop_ptr;
memset(&capi_uv_ptr, 0, sizeof(capi_uv_ptr));
memset(&in_model_buffer_param_, 0, sizeof(in_model_buffer_param_));
memset(&scratch_param_, 0, sizeof(scratch_param_));
PAL_VERBOSE(LOG_TAG, "Issuing capi_init");
rc = capi_init_(capi_handle_, &init_set_proplist);
if (rc != CAPI_V2_EOK) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "capi_init status is %d, exiting, status %d",
rc, status);
goto exit;
}
if (!capi_handle_->vtbl_ptr) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "capi_handle->vtbl_ptr is nullptr, exiting, status %d",
status);
goto exit;
}
buffer_thread_handler_ =
std::thread(SoundTriggerEngineCapi::BufferThreadLoop, this);
if (!buffer_thread_handler_.joinable()) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "failed to create buffer thread = %d", status);
goto exit;
}
if (detection_type_ == ST_SM_TYPE_USER_VERIFICATION) {
PAL_VERBOSE(LOG_TAG, "Issuing capi_get STAGE2_UV_WRAPPER_ID_INMODEL_BUFFER_SIZE");
capi_uv_ptr.data_ptr = (int8_t *)&in_model_buffer_param_;
capi_uv_ptr.actual_data_len = sizeof(in_model_buffer_param_);
capi_uv_ptr.max_data_len = sizeof(in_model_buffer_param_);
rc = capi_handle_->vtbl_ptr->get_param(capi_handle_,
STAGE2_UV_WRAPPER_ID_INMODEL_BUFFER_SIZE,
NULL,
&capi_uv_ptr);
if (CAPI_V2_EFAILED == rc) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "capi_get STAGE2_UV_WRAPPER_ID_INMODEL_BUFFER_SIZE param failed, %d",
rc);
goto exit;
}
if (in_model_buffer_param_.scratch_size == 0) {
capi_uv_ptr.data_ptr = (int8_t *)&scratch_param_;
capi_uv_ptr.actual_data_len = sizeof(scratch_param_);
capi_uv_ptr.max_data_len = sizeof(scratch_param_);
PAL_VERBOSE(LOG_TAG, "Issuing capi_get STAGE2_UV_WRAPPER_ID_SCRATCH_PARAM");
rc = capi_handle_->vtbl_ptr->get_param(capi_handle_,
STAGE2_UV_WRAPPER_ID_SCRATCH_PARAM,
NULL,
&capi_uv_ptr);
if (CAPI_V2_EFAILED == rc) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "capi get param STAGE2_UV_WRAPPER_ID_SCRATCH_PARAM failed, %d",
rc);
goto exit;
}
capi_uv_ptr.data_ptr = (int8_t *)&scratch_param_;
capi_uv_ptr.actual_data_len = sizeof(scratch_param_);
capi_uv_ptr.max_data_len = sizeof(scratch_param_);
scratch_param_.scratch_ptr = (int8_t *)calloc(1, scratch_param_.scratch_size);
if (scratch_param_.scratch_ptr == NULL) {
PAL_ERR(LOG_TAG, "failed to allocate the scratch memory");
return -ENOMEM;
}
PAL_VERBOSE(LOG_TAG, "Issuing capi_set STAGE2_UV_WRAPPER_ID_SCRATCH_PARAM");
rc = capi_handle_->vtbl_ptr->set_param(capi_handle_,
STAGE2_UV_WRAPPER_ID_SCRATCH_PARAM,
NULL,
&capi_uv_ptr);
if (CAPI_V2_EFAILED == rc) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "capi set param STAGE2_UV_WRAPPER_ID_SCRATCH_PARAM failed, status %d", status);
free(scratch_param_.scratch_ptr);
scratch_param_.scratch_ptr = NULL;
}
}
}
exit:
PAL_DBG(LOG_TAG, "Exit, status %d", status);
return status;
}
int32_t SoundTriggerEngineCapi::UnloadSoundModel(Stream *s __unused)
{
int32_t status = 0;
PAL_DBG(LOG_TAG, "Enter, Issuing capi_end");
std::lock_guard<std::mutex> lck(mutex_);
status = StopSoundEngine();
if (status) {
PAL_ERR(LOG_TAG, "Failed to stop sound engine, status = %d", status);
}
status = capi_handle_->vtbl_ptr->end(capi_handle_);
if (status != CAPI_V2_EOK) {
PAL_ERR(LOG_TAG, "Capi end function failed, status = %d",
status);
status = -EINVAL;
goto exit;
}
exit:
if (scratch_param_.scratch_ptr) {
free(scratch_param_.scratch_ptr);
scratch_param_.scratch_ptr = NULL;
}
PAL_DBG(LOG_TAG, "Exit, status %d", status);
return status;
}
int32_t SoundTriggerEngineCapi::StartRecognition(Stream *s __unused)
{
int32_t status = 0;
PAL_DBG(LOG_TAG, "Enter");
std::lock_guard<std::mutex> lck(mutex_);
status = StartSoundEngine();
if (0 != status) {
PAL_ERR(LOG_TAG, "Failed to start sound engine, status = %d", status);
goto exit;
}
exit:
PAL_DBG(LOG_TAG, "Exit, status %d", status);
return status;
}
int32_t SoundTriggerEngineCapi::RestartRecognition(Stream *s __unused)
{
int32_t status = 0;
PAL_DBG(LOG_TAG, "Enter");
std::lock_guard<std::mutex> lck(mutex_);
processing_started_ = false;
{
exit_buffering_ = true;
std::lock_guard<std::mutex> event_lck(event_mutex_);
}
if (reader_) {
reader_->reset();
} else {
status = -EINVAL;
goto exit;
}
exit:
PAL_DBG(LOG_TAG, "Exit, status %d", status);
return status;
}
int32_t SoundTriggerEngineCapi::StopRecognition(Stream *s __unused)
{
int32_t status = 0;
PAL_DBG(LOG_TAG, "Enter");
std::lock_guard<std::mutex> lck(mutex_);
processing_started_ = false;
{
exit_buffering_ = true;
std::lock_guard<std::mutex> event_lck(event_mutex_);
}
if (reader_) {
reader_->reset();
} else {
status = -EINVAL;
goto exit;
}
exit:
PAL_DBG(LOG_TAG, "Exit, status %d", status);
return status;
}
int32_t SoundTriggerEngineCapi::UpdateConfLevels(
Stream *s __unused,
struct pal_st_recognition_config *config __unused,
uint8_t *conf_levels,
uint32_t num_conf_levels)
{
int32_t status = 0;
PAL_DBG(LOG_TAG, "Enter");
if (!conf_levels || !num_conf_levels) {
status = -EINVAL;
PAL_ERR(LOG_TAG, "Invalid config, status %d", status);
return status;
}
std::lock_guard<std::mutex> lck(mutex_);
confidence_threshold_ = *(int32_t *)conf_levels;
PAL_DBG(LOG_TAG, "confidence threshold: %d", confidence_threshold_);
return status;
}
void SoundTriggerEngineCapi::SetDetected(bool detected)
{
PAL_DBG(LOG_TAG, "SetDetected %d", detected);
std::lock_guard<std::mutex> lck(event_mutex_);
if (detected != processing_started_) {
if (detected)
reader_->updateState(READER_ENABLED);
processing_started_ = detected;
exit_buffering_ = !processing_started_;
PAL_INFO(LOG_TAG, "setting processing started %d", detected);
cv_.notify_one();
} else {
PAL_VERBOSE(LOG_TAG, "processing started unchanged");
}
}