| /* |
| * 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 Qualcomm Innovation Center, Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted (subject to the limitations in the |
| * disclaimer below) 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 Qualcomm Innovation Center, Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE |
| * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT |
| * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 "sthal_SoundTriggerSession" |
| #define ATRACE_TAG (ATRACE_TAG_AUDIO | ATRACE_TAG_HAL) |
| #define LOG_NDEBUG 0 |
| /*#define VERY_VERY_VERBOSE_LOGGING*/ |
| #ifdef VERY_VERY_VERBOSE_LOGGING |
| #define ALOGVV ALOGV |
| #else |
| #define ALOGVV(a...) do { } while(0) |
| #endif |
| |
| #include "SoundTriggerSession.h" |
| |
| #include <log/log.h> |
| #include <utils/Trace.h> |
| #include <cstring> |
| #include <chrono> |
| #include <thread> |
| |
| #include "PalApi.h" |
| |
| SoundTriggerSession::SoundTriggerSession(sound_model_handle_t handle, |
| audio_hw_call_back_t callback) |
| { |
| state_ = IDLE; |
| sm_handle_ = handle; |
| rec_config_payload_ = nullptr; |
| rec_config_ = nullptr; |
| hal_callback_ = callback; |
| pal_handle_ = nullptr; |
| } |
| |
| SoundTriggerSession::~SoundTriggerSession() |
| { |
| if (pal_handle_ && state_ != IDLE) |
| pal_stream_close(pal_handle_); |
| pal_handle_ = nullptr; |
| |
| if (rec_config_payload_) { |
| free(rec_config_payload_); |
| rec_config_payload_ = nullptr; |
| } |
| rec_config_ = nullptr; |
| } |
| |
| int SoundTriggerSession::pal_callback( |
| pal_stream_handle_t *stream_handle, |
| uint32_t event_id, |
| uint32_t *event_data, |
| uint32_t event_size, |
| uint64_t cookie) |
| { |
| int32_t status = 0; |
| bool lock_status = false; |
| unsigned int size = 0; |
| int i = 0; |
| int j = 0; |
| recognition_callback_t callback; |
| SoundTriggerSession *session = nullptr; |
| struct pal_st_recognition_event *event = nullptr; |
| struct sound_trigger_recognition_event *st_event = nullptr; |
| struct sound_trigger_phrase_recognition_event *phrase_event = nullptr; |
| struct pal_st_phrase_recognition_event *pal_phrase_event = nullptr; |
| |
| if (!stream_handle || !event_data) { |
| status = -EINVAL; |
| ALOGE("%s: error, invalid stream handle or event data", __func__); |
| goto exit; |
| } |
| |
| ALOGD("%s: stream_handle (%p), event_id (%x), event_size (%d)," |
| "cookie (%" PRIu64 ")", __func__, stream_handle, event_id, |
| event_size, cookie); |
| |
| session = (SoundTriggerSession *)cookie; |
| /* |
| * Sometimes Client may call unload directly, which may get blocked |
| * in PAL when releasing second stage engine thread, as it is waiting |
| * for this callback to finish. Check if session state changes to non |
| * ACTIVE state. |
| */ |
| do { |
| lock_status = session->ses_mutex_.try_lock(); |
| } while(!lock_status && (session->state_ == ACTIVE)); |
| |
| if (session->state_ != ACTIVE) { |
| ALOGW("%s: skip notification as client has stopped", __func__); |
| goto exit; |
| } |
| |
| event = (struct pal_st_recognition_event *)event_data; |
| |
| if (event->type == PAL_SOUND_MODEL_TYPE_GENERIC) { |
| // parse event and check if keyword detected |
| size = sizeof(struct sound_trigger_recognition_event) + |
| event->data_size; |
| st_event = (struct sound_trigger_recognition_event *)calloc(1, size); |
| if (!st_event) { |
| status = -ENOMEM; |
| ALOGE("%s: error, failed to allocate recognition event", __func__); |
| goto exit; |
| } |
| |
| st_event->data_offset = sizeof(struct sound_trigger_recognition_event); |
| } else if (event->type == PAL_SOUND_MODEL_TYPE_KEYPHRASE) { |
| size = sizeof(struct sound_trigger_phrase_recognition_event) + |
| event->data_size; |
| phrase_event = |
| (struct sound_trigger_phrase_recognition_event *)calloc(1, size); |
| if (!phrase_event) { |
| status = -ENOMEM; |
| ALOGE("%s: error, failed to allocate recognition event", __func__); |
| goto exit; |
| } |
| |
| st_event = (struct sound_trigger_recognition_event *)phrase_event; |
| st_event->data_offset = |
| sizeof(struct sound_trigger_phrase_recognition_event); |
| |
| // copy data only related to phrase event |
| pal_phrase_event = (struct pal_st_phrase_recognition_event *)event; |
| phrase_event->num_phrases = pal_phrase_event->num_phrases; |
| for (i = 0; i < phrase_event->num_phrases; i++) { |
| phrase_event->phrase_extras[i].id = |
| pal_phrase_event->phrase_extras[i].id; |
| phrase_event->phrase_extras[i].recognition_modes = |
| pal_phrase_event->phrase_extras[i].recognition_modes; |
| phrase_event->phrase_extras[i].confidence_level = |
| pal_phrase_event->phrase_extras[i].confidence_level; |
| phrase_event->phrase_extras[i].num_levels = |
| pal_phrase_event->phrase_extras[i].num_levels; |
| |
| for (j = 0; j < phrase_event->phrase_extras[i].num_levels; j++) { |
| phrase_event->phrase_extras[i].levels[j].user_id = |
| pal_phrase_event->phrase_extras[i].levels[j].user_id; |
| phrase_event->phrase_extras[i].levels[j].level = |
| pal_phrase_event->phrase_extras[i].levels[j].level; |
| } |
| } |
| } else { |
| ALOGE("%s: Invalid event type :%d", __func__, event->type); |
| status = -EINVAL; |
| goto exit; |
| } |
| |
| // copy members inside structrue |
| st_event->status = event->status; |
| st_event->type = (sound_trigger_sound_model_type_t)event->type; |
| st_event->model = session->GetSoundModelHandle(); |
| st_event->capture_available = event->capture_available; |
| st_event->capture_session = session->GetCaptureHandle(); |
| st_event->capture_delay_ms = event->capture_delay_ms; |
| st_event->capture_preamble_ms = event->capture_preamble_ms; |
| st_event->trigger_in_data = event->trigger_in_data; |
| |
| st_event->audio_config.sample_rate = event->media_config.sample_rate; |
| if (event->media_config.ch_info.channels == 1) |
| st_event->audio_config.channel_mask = AUDIO_CHANNEL_IN_MONO; |
| else if (event->media_config.ch_info.channels == 2) |
| st_event->audio_config.channel_mask = AUDIO_CHANNEL_IN_STEREO; |
| st_event->audio_config.format = AUDIO_FORMAT_PCM_16_BIT; |
| |
| st_event->data_size = event->data_size; |
| |
| // copy opaque data |
| memcpy((uint8_t *)st_event + st_event->data_offset, |
| (uint8_t *)event + event->data_offset, |
| st_event->data_size); |
| |
| // callback to SoundTriggerService |
| session->GetRecognitionCallback(&callback); |
| session->ses_mutex_.unlock(); |
| lock_status = false; |
| ATRACE_BEGIN("sthal: client detection callback"); |
| if (session->state_ == ACTIVE) |
| callback(st_event, session->GetCookie()); |
| else |
| ALOGW("%s: skip detection callback as client has stopped", __func__); |
| ATRACE_END(); |
| |
| exit: |
| // release resources allocated |
| if (phrase_event) |
| free(phrase_event); |
| else if (st_event) |
| free(st_event); |
| if (lock_status) |
| session->ses_mutex_.unlock(); |
| ALOGV("%s: Exit, status %d", __func__, status); |
| |
| return status; |
| } |
| |
| bool SoundTriggerSession::IsACDSoundModel(struct sound_trigger_sound_model *sound_model) |
| { |
| //todo: get this from PAL instead of hardcoding. |
| const sound_trigger_uuid_t qc_acd_uuid = { 0x4e93281b, 0x296e, 0x4d73, 0x9833, |
| { 0x27, 0x10, 0xc3, 0xc7, 0xc1, 0xdb } }; |
| |
| if (sound_model && |
| !std::memcmp(&sound_model->vendor_uuid, &qc_acd_uuid, |
| sizeof(sound_trigger_uuid_t))) |
| return true; |
| else |
| return false; |
| } |
| |
| int SoundTriggerSession::OpenPALStream(pal_stream_type_t stream_type) |
| { |
| int status = 0; |
| struct pal_stream_attributes stream_attributes; |
| struct pal_device device; |
| |
| ALOGV("%s: Enter", __func__); |
| |
| device.id = PAL_DEVICE_IN_HANDSET_VA_MIC; // To-Do: convert into PAL Device |
| device.config.sample_rate = 48000; |
| device.config.bit_width = 16; |
| device.config.ch_info.channels = 2; |
| device.config.ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL; |
| device.config.ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR; |
| device.config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE; |
| |
| stream_attributes.type = stream_type; |
| stream_attributes.flags = (pal_stream_flags_t)0; |
| stream_attributes.direction = PAL_AUDIO_INPUT; |
| stream_attributes.in_media_config.sample_rate = 16000; |
| stream_attributes.in_media_config.bit_width = 16; |
| stream_attributes.in_media_config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE; |
| stream_attributes.in_media_config.ch_info.channels = 1; |
| stream_attributes.in_media_config.ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL; |
| |
| ALOGD("%s:(%x:status)%d", __func__, status, __LINE__); |
| status = pal_stream_open(&stream_attributes, |
| 1, |
| &device, |
| 0, |
| nullptr, |
| &pal_callback, |
| (uint64_t)this, |
| &pal_handle_); |
| |
| ALOGD("%s:(%x:status)%d", __func__, status, __LINE__); |
| |
| if (status) { |
| ALOGE("%s: Pal Stream Open Error (%x)", __func__, status); |
| status = -EINVAL; |
| goto exit; |
| } |
| |
| exit: |
| ALOGV("%s: Exit, status = %d", __func__, status); |
| |
| return status; |
| } |
| |
| int SoundTriggerSession::StopRecognition_l() |
| { |
| int status = 0; |
| |
| ALOGV("%s: Enter", __func__); |
| |
| // deregister from audio hal |
| RegisterHalEvent(false); |
| |
| // stop pal stream |
| status = pal_stream_stop(pal_handle_); |
| if (status) { |
| ALOGE("%s: error, failed to stop pal stream, status = %d", |
| __func__, status); |
| } |
| |
| if (rec_config_payload_) { |
| free(rec_config_payload_); |
| rec_config_payload_ = nullptr; |
| } |
| rec_config_ = nullptr; |
| |
| state_ = STOPPED; |
| ALOGV("%s: Exit, status = %d", __func__, status); |
| |
| return status; |
| } |
| |
| int SoundTriggerSession::LoadSoundModel( |
| struct sound_trigger_sound_model *sound_model) |
| { |
| int status = 0; |
| struct pal_st_sound_model *pal_common_sm = nullptr; |
| struct pal_st_phrase_sound_model *pal_phrase_sm = nullptr; |
| struct sound_trigger_sound_model *common_sm = nullptr; |
| struct sound_trigger_phrase_sound_model *phrase_sm = nullptr; |
| pal_param_payload *param_payload = nullptr; |
| unsigned int size = 0; |
| pal_stream_type_t stream_type = PAL_STREAM_VOICE_UI; |
| |
| ALOGV("%s: Enter", __func__); |
| std::lock_guard<std::mutex> lck(ses_mutex_); |
| |
| if (IsACDSoundModel(sound_model)) |
| stream_type = PAL_STREAM_ACD; |
| |
| // open pal stream |
| status = OpenPALStream(stream_type); |
| if (status) { |
| ALOGE("%s: error, failed to open PAL stream", __func__); |
| goto exit; |
| } |
| |
| // parse sound model into pal_sound_model |
| if (sound_model->type == SOUND_MODEL_TYPE_GENERIC) { |
| common_sm = sound_model; |
| if ((stream_type != PAL_STREAM_ACD) && |
| (!common_sm->data_size || |
| (common_sm->data_offset < sizeof(*common_sm)))) { |
| ALOGE("%s: Invalid Generic sound model params " |
| "data size=%d, data offset=%d", __func__, |
| common_sm->data_size, common_sm->data_offset); |
| status = -EINVAL; |
| goto exit; |
| } |
| |
| size = sizeof(struct pal_st_sound_model) + |
| common_sm->data_size; |
| param_payload = (pal_param_payload *)calloc(1, |
| sizeof(pal_param_payload) + size); |
| if (!param_payload) { |
| ALOGE("%s: error, failed to allocate pal sound model", __func__); |
| status = -ENOMEM; |
| goto exit; |
| } |
| param_payload->payload_size = size; |
| pal_common_sm = (struct pal_st_sound_model *)param_payload->payload; |
| |
| pal_common_sm->type = (pal_st_sound_model_type_t)common_sm->type; |
| memcpy(&pal_common_sm->uuid, &common_sm->uuid, |
| sizeof(struct st_uuid)); |
| memcpy(&pal_common_sm->vendor_uuid, &common_sm->vendor_uuid, |
| sizeof(struct st_uuid)); |
| pal_common_sm->data_size = common_sm->data_size; |
| pal_common_sm->data_offset = sizeof(struct pal_st_sound_model); |
| |
| // data_size is zero for ACD streams and non-zero for other streams |
| if (pal_common_sm->data_size) |
| memcpy((uint8_t *)pal_common_sm + pal_common_sm->data_offset, |
| (uint8_t *)common_sm + common_sm->data_offset, |
| pal_common_sm->data_size); |
| |
| } else if (sound_model->type == SOUND_MODEL_TYPE_KEYPHRASE) { |
| phrase_sm = (struct sound_trigger_phrase_sound_model *)sound_model; |
| if ((phrase_sm->common.data_size == 0) || |
| (phrase_sm->common.data_offset < sizeof(*phrase_sm)) || |
| (phrase_sm->common.type != SOUND_MODEL_TYPE_KEYPHRASE) || |
| (phrase_sm->num_phrases == 0)) { |
| ALOGE("%s: Invalid phrase sound model params " |
| "data size=%d, data offset=%d, type=%d phrases=%d", |
| __func__, phrase_sm->common.data_size, |
| phrase_sm->common.data_offset, phrase_sm->common.type, |
| phrase_sm->num_phrases); |
| status = -EINVAL; |
| goto exit; |
| } |
| |
| size = sizeof(struct pal_st_phrase_sound_model) + |
| phrase_sm->common.data_size; |
| param_payload = (pal_param_payload *)calloc(1, |
| sizeof(pal_param_payload) + size); |
| if (!param_payload) { |
| ALOGE("%s: error, failed to allocate pal phrase sound model", |
| __func__); |
| status = -ENOMEM; |
| goto exit; |
| } |
| param_payload->payload_size = size; |
| pal_phrase_sm = |
| (struct pal_st_phrase_sound_model *)param_payload->payload; |
| |
| pal_phrase_sm->common.type = |
| (pal_st_sound_model_type_t)phrase_sm->common.type; |
| memcpy(&pal_phrase_sm->common.uuid, &phrase_sm->common.uuid, |
| sizeof(struct st_uuid)); |
| memcpy(&pal_phrase_sm->common.vendor_uuid, |
| &phrase_sm->common.vendor_uuid, |
| sizeof(struct st_uuid)); |
| pal_phrase_sm->common.data_size = phrase_sm->common.data_size; |
| pal_phrase_sm->common.data_offset = |
| sizeof(struct pal_st_phrase_sound_model); |
| pal_phrase_sm->num_phrases = phrase_sm->num_phrases; |
| |
| for (int i = 0; i < pal_phrase_sm->num_phrases; i++) { |
| pal_phrase_sm->phrases[i].id = phrase_sm->phrases[i].id; |
| pal_phrase_sm->phrases[i].recognition_mode = |
| phrase_sm->phrases[i].recognition_mode; |
| pal_phrase_sm->phrases[i].num_users = |
| phrase_sm->phrases[i].num_users; |
| for (int j = 0; j < pal_phrase_sm->phrases[i].num_users; j++) |
| pal_phrase_sm->phrases[i].users[j] = |
| phrase_sm->phrases[i].users[j]; |
| |
| memcpy(pal_phrase_sm->phrases[i].locale, |
| phrase_sm->phrases[i].locale, |
| SOUND_TRIGGER_MAX_LOCALE_LEN); |
| memcpy(pal_phrase_sm->phrases[i].text, |
| phrase_sm->phrases[i].text, |
| SOUND_TRIGGER_MAX_STRING_LEN); |
| } |
| |
| memcpy((uint8_t *)pal_phrase_sm + pal_phrase_sm->common.data_offset, |
| (uint8_t *)phrase_sm + phrase_sm->common.data_offset, |
| pal_phrase_sm->common.data_size); |
| pal_common_sm = (struct pal_st_sound_model *)pal_phrase_sm; |
| } else { |
| ALOGE("%s: error, unknown sound model type %d", |
| __func__, sound_model->type); |
| status = -EINVAL; |
| goto exit; |
| } |
| |
| // load sound model with pal api |
| status = pal_stream_set_param(pal_handle_, |
| PAL_PARAM_ID_LOAD_SOUND_MODEL, |
| param_payload); |
| if (status) { |
| ALOGE("%s: error, failed to load sound model into PAL, status = %d", |
| __func__, status); |
| goto exit; |
| } |
| |
| state_ = LOADED; |
| |
| exit: |
| if (param_payload) |
| free(param_payload); |
| ALOGV("%s: Exit, status = %d", __func__, status); |
| |
| return status; |
| } |
| |
| int SoundTriggerSession::UnloadSoundModel() |
| { |
| int status = 0; |
| |
| ALOGV("%s: Enter", __func__); |
| std::lock_guard<std::mutex> lck(ses_mutex_); |
| if (state_ == ACTIVE) { |
| status = StopRecognition_l(); |
| if (status) { |
| ALOGE("%s: error, failed to stop recognition, status = %d", |
| __func__, status); |
| } |
| } |
| |
| status = pal_stream_close(pal_handle_); |
| if (status) { |
| ALOGE("%s: error, failed to close pal stream, status = %d", |
| __func__, status); |
| } |
| pal_handle_ = nullptr; |
| if (rec_config_payload_) { |
| free(rec_config_payload_); |
| rec_config_payload_ = nullptr; |
| } |
| rec_config_ = nullptr; |
| |
| state_ = IDLE; |
| |
| ALOGV("%s: Exit, status = %d", __func__, status); |
| |
| return status; |
| } |
| |
| int SoundTriggerSession::StartRecognition( |
| const struct sound_trigger_recognition_config *config, |
| recognition_callback_t callback, |
| void *cookie, |
| uint32_t version) |
| { |
| int status = 0; |
| unsigned int size = 0; |
| |
| ALOGV("%s: Enter, state = %d", __func__, state_); |
| std::lock_guard<std::mutex> lck(ses_mutex_); |
| |
| if (rec_config_payload_) { |
| free(rec_config_payload_); // valid due to subsequent start after a detection |
| rec_config_payload_ = nullptr; |
| } |
| size = sizeof(struct pal_st_recognition_config) + |
| config->data_size; |
| rec_config_payload_ = (pal_param_payload *)calloc(1, |
| sizeof(pal_param_payload) + size); |
| if (!rec_config_payload_) { |
| ALOGE("%s: error, failed to allocate pal recognition config", __func__); |
| status = -ENOMEM; |
| goto exit; |
| } |
| rec_config_payload_->payload_size = size; |
| rec_config_ = (struct pal_st_recognition_config *)rec_config_payload_->payload; |
| |
| rec_config_->capture_handle = (int32_t)config->capture_handle; |
| rec_config_->capture_device = (uint32_t)config->capture_device; |
| rec_config_->capture_requested = config->capture_requested; |
| rec_config_->num_phrases = config->num_phrases; |
| for (int i = 0; i < rec_config_->num_phrases; i++) { |
| rec_config_->phrases[i].id = config->phrases[i].id; |
| rec_config_->phrases[i].recognition_modes = |
| config->phrases[i].recognition_modes; |
| rec_config_->phrases[i].confidence_level = |
| config->phrases[i].confidence_level; |
| rec_config_->phrases[i].num_levels = config->phrases[i].num_levels; |
| |
| for (int j = 0; j < rec_config_->phrases[i].num_levels; j++) { |
| rec_config_->phrases[i].levels[j].user_id = |
| config->phrases[i].levels[j].user_id; |
| rec_config_->phrases[i].levels[j].level = |
| config->phrases[i].levels[j].level; |
| } |
| } |
| rec_config_->data_size = config->data_size; |
| rec_config_->data_offset = sizeof(struct pal_st_recognition_config); |
| rec_config_->cookie = (uint8_t *)this; |
| if (version == SOUND_TRIGGER_DEVICE_API_VERSION_1_3) { |
| memcpy((uint8_t *)rec_config_ + rec_config_->data_offset, |
| (uint8_t *)config + config->data_offset - |
| sizeof(struct sound_trigger_recognition_config_header), |
| rec_config_->data_size); |
| } else { |
| memcpy((uint8_t *)rec_config_ + rec_config_->data_offset, |
| (uint8_t *)config + config->data_offset, |
| rec_config_->data_size); |
| } |
| |
| // set recognition config |
| status = pal_stream_set_param(pal_handle_, |
| PAL_PARAM_ID_RECOGNITION_CONFIG, |
| rec_config_payload_); |
| if (status) { |
| ALOGE("%s: error, failed to set recognition config, status = %d", |
| __func__, status); |
| goto exit; |
| } |
| |
| rec_callback_ = callback; |
| cookie_ = cookie; |
| |
| // start pal stream |
| status = pal_stream_start(pal_handle_); |
| if (status) { |
| ALOGE("%s: error, failed to start pal stream, status = %d", |
| __func__, status); |
| goto exit; |
| } |
| state_ = ACTIVE; |
| |
| // register to audio hal |
| RegisterHalEvent(true); |
| |
| exit: |
| if (status && rec_config_payload_) { |
| free(rec_config_payload_); |
| rec_config_payload_ = nullptr; |
| rec_config_ = nullptr; |
| } |
| |
| ALOGV("%s: Exit, status = %d", __func__, status); |
| |
| return status; |
| } |
| |
| int SoundTriggerSession::StopRecognition() |
| { |
| int status = 0; |
| |
| ALOGV("%s: Enter", __func__); |
| std::lock_guard<std::mutex> lck(ses_mutex_); |
| |
| // deregister from audio hal |
| RegisterHalEvent(false); |
| |
| // stop pal stream |
| status = pal_stream_stop(pal_handle_); |
| if (status) { |
| ALOGE("%s: error, failed to stop pal stream, status = %d", |
| __func__, status); |
| } |
| |
| if (rec_config_payload_) { |
| free(rec_config_payload_); |
| rec_config_payload_ = nullptr; |
| } |
| rec_config_ = nullptr; |
| |
| state_ = STOPPED; |
| ALOGV("%s: Exit, status = %d", __func__, status); |
| |
| return status; |
| } |
| |
| void SoundTriggerSession::RegisterHalEvent(bool is_register) |
| { |
| struct sound_trigger_event_info event_info; |
| |
| if ((rec_config_ && rec_config_->capture_requested) && hal_callback_) { |
| if (is_register) { |
| ALOGD("%s:[c%d] ST_EVENT_SESSION_REGISTER capture_handle %d", |
| __func__, sm_handle_, rec_config_->capture_handle); |
| event_info.st_ses.p_ses = (void *)pal_handle_; |
| event_info.st_ses.capture_handle = rec_config_->capture_handle; |
| hal_callback_(ST_EVENT_SESSION_REGISTER, &event_info); |
| } else { |
| ALOGD("%s:[c%d] ST_EVENT_SESSION_DEREGISTER capture_handle %d", |
| __func__, sm_handle_, rec_config_->capture_handle); |
| event_info.st_ses.p_ses = (void *)pal_handle_; |
| event_info.st_ses.capture_handle = rec_config_->capture_handle; |
| hal_callback_(ST_EVENT_SESSION_DEREGISTER, &event_info); |
| } |
| } |
| } |
| |
| sound_model_handle_t SoundTriggerSession::GetSoundModelHandle() |
| { |
| return sm_handle_; |
| } |
| |
| int SoundTriggerSession::GetCaptureHandle() |
| { |
| int handle = 0; |
| |
| if (rec_config_) |
| handle = rec_config_->capture_handle; |
| |
| return handle; |
| } |
| |
| int SoundTriggerSession::GetModuleVersion(char version[]) |
| { |
| int status = 0; |
| pal_param_payload *param_payload = nullptr; |
| struct version_arch_payload *version_payload = nullptr; |
| |
| ALOGV("%s: Enter", __func__); |
| std::lock_guard<std::mutex> lck(ses_mutex_); |
| status = OpenPALStream(PAL_STREAM_VOICE_UI); |
| if (status) { |
| ALOGE("%s: Failed to open pal stream, status = %d", __func__, status); |
| goto exit; |
| } |
| |
| status = pal_stream_get_param(pal_handle_, |
| PAL_PARAM_ID_WAKEUP_MODULE_VERSION, ¶m_payload); |
| if (status) { |
| ALOGE("%s: Failed to get version, status = %d", __func__, status); |
| goto exit; |
| } |
| |
| version_payload = (struct version_arch_payload *)param_payload; |
| snprintf(version, SOUND_TRIGGER_MAX_STRING_LEN, "%d, %s", |
| version_payload->version, version_payload->arch); |
| |
| exit: |
| if (pal_handle_) { |
| status = pal_stream_close(pal_handle_); |
| if (status) { |
| ALOGE("%s: error, failed to close pal stream, status = %d", |
| __func__, status); |
| } |
| pal_handle_ = nullptr; |
| } |
| ALOGV("%s: Exit", __func__); |
| return 0; |
| } |
| |
| void *SoundTriggerSession::GetCookie() |
| { |
| return cookie_; |
| } |
| |
| void SoundTriggerSession::GetRecognitionCallback( |
| recognition_callback_t *callback) |
| { |
| *callback = rec_callback_; |
| } |
| |