| /* |
| * 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 "PAL: StreamSoundTrigger" |
| |
| #include "StreamSoundTrigger.h" |
| |
| #include <chrono> |
| #include <unistd.h> |
| |
| #include "Session.h" |
| #include "SessionAlsaPcm.h" |
| #include "ResourceManager.h" |
| #include "Device.h" |
| #include "kvh2xml.h" |
| #include "VoiceUIInterface.h" |
| |
| // TODO: find another way to print debug logs by default |
| #define ST_DBG_LOGS |
| #ifdef ST_DBG_LOGS |
| #define PAL_DBG(LOG_TAG,...) PAL_INFO(LOG_TAG,__VA_ARGS__) |
| #endif |
| |
| #define ST_DEFERRED_STOP_DEALY_MS (1000) |
| #define ST_MODEL_TYPE_SHIFT (16) |
| #define ST_MAX_FSTAGE_CONF_LEVEL (100) |
| |
| ST_DBG_DECLARE(static int lab_cnt = 0); |
| |
| StreamSoundTrigger::StreamSoundTrigger(struct pal_stream_attributes *sattr, |
| struct pal_device *dattr, |
| uint32_t no_of_devices, |
| struct modifier_kv *modifiers __unused, |
| uint32_t no_of_modifiers __unused, |
| std::shared_ptr<ResourceManager> rm) { |
| |
| class SoundTriggerUUID uuid; |
| int32_t enable_concurrency_count = 0; |
| int32_t disable_concurrency_count = 0; |
| reader_ = nullptr; |
| detection_state_ = ENGINE_IDLE; |
| notification_state_ = ENGINE_IDLE; |
| model_id_ = 0; |
| sm_config_ = nullptr; |
| rec_config_ = nullptr; |
| recognition_mode_ = 0; |
| paused_ = false; |
| device_opened_ = false; |
| pending_stop_ = false; |
| currentState = STREAM_IDLE; |
| capture_requested_ = false; |
| hist_buf_duration_ = 0; |
| pre_roll_duration_ = 0; |
| conf_levels_intf_version_ = 0; |
| st_conf_levels_ = nullptr; |
| st_conf_levels_v2_ = nullptr; |
| lab_fd_ = nullptr; |
| rejection_notified_ = false; |
| mutex_unlocked_after_cb_ = false; |
| common_cp_update_disable_ = false; |
| second_stage_processing_ = false; |
| gsl_engine_model_ = nullptr; |
| gsl_conf_levels_ = nullptr; |
| gsl_engine_ = nullptr; |
| vui_intf_ = nullptr; |
| sm_cfg_ = nullptr; |
| ec_rx_dev_ = nullptr; |
| mDevices.clear(); |
| |
| // Setting default volume to unity |
| mVolumeData = (struct pal_volume_data *)malloc(sizeof(struct pal_volume_data) |
| +sizeof(struct pal_channel_vol_kv)); |
| if (mVolumeData == NULL) { |
| PAL_ERR(LOG_TAG, "Failed to allocate memory for volume data"); |
| throw std::runtime_error("Failed to allocate memory for volume data"); |
| } |
| mVolumeData->no_of_volpair = 1; |
| mVolumeData->volume_pair[0].channel_mask = 0x03; |
| mVolumeData->volume_pair[0].vol = 1.0f; |
| |
| PAL_DBG(LOG_TAG, "Enter"); |
| // TODO: handle modifiers later |
| mNoOfModifiers = 0; |
| mModifiers = (struct modifier_kv *) (nullptr); |
| |
| // get voice UI platform info |
| vui_ptfm_info_ = VoiceUIPlatformInfo::GetInstance(); |
| if (!vui_ptfm_info_) { |
| PAL_ERR(LOG_TAG, "Failed to get voice UI platform info"); |
| throw std::runtime_error("Failed to get voice UI platform info"); |
| } |
| |
| if (!dattr) { |
| PAL_ERR(LOG_TAG,"Error:invalid device arguments"); |
| throw std::runtime_error("invalid device arguments"); |
| } |
| |
| mStreamAttr = (struct pal_stream_attributes *)calloc(1, |
| sizeof(struct pal_stream_attributes)); |
| if (!mStreamAttr) { |
| PAL_ERR(LOG_TAG, "stream attributes allocation failed"); |
| throw std::runtime_error("stream attributes allocation failed"); |
| } |
| |
| ar_mem_cpy(mStreamAttr, sizeof(pal_stream_attributes), |
| sattr, sizeof(pal_stream_attributes)); |
| |
| PAL_VERBOSE(LOG_TAG, "Create new Devices with no_of_devices - %d", |
| no_of_devices); |
| |
| /* assume only one input device */ |
| if (no_of_devices > 1) { |
| std::string err; |
| err = "incorrect number of devices expected 1, got " + |
| std::to_string(no_of_devices); |
| PAL_ERR(LOG_TAG, "%s", err.c_str()); |
| free(mStreamAttr); |
| throw std::runtime_error(err); |
| } |
| |
| // Create internal states |
| st_idle_ = new StIdle(*this); |
| st_loaded_ = new StLoaded(*this); |
| st_active = new StActive(*this); |
| st_detected_ = new StDetected(*this); |
| st_buffering_ = new StBuffering(*this); |
| st_ssr_ = new StSSR(*this); |
| |
| AddState(st_idle_); |
| AddState(st_loaded_); |
| AddState(st_active); |
| AddState(st_detected_); |
| AddState(st_buffering_); |
| AddState(st_ssr_); |
| |
| // Set initial state |
| if (rm->cardState == CARD_STATUS_OFFLINE) { |
| cur_state_ = st_ssr_; |
| prev_state_ = nullptr; |
| state_for_restore_ = ST_STATE_IDLE; |
| } else { |
| cur_state_ = st_idle_; |
| prev_state_ = nullptr; |
| state_for_restore_ = ST_STATE_NONE; |
| } |
| |
| rm->registerStream(this); |
| |
| // Print the concurrency feature flags supported |
| PAL_INFO(LOG_TAG, "capture conc enable %d,voice conc enable %d,voip conc enable %d", |
| vui_ptfm_info_->GetConcurrentCaptureEnable(), vui_ptfm_info_->GetConcurrentVoiceCallEnable(), |
| vui_ptfm_info_->GetConcurrentVoipCallEnable()); |
| |
| // check concurrency count from rm |
| rm->GetSoundTriggerConcurrencyCount(PAL_STREAM_VOICE_UI, &enable_concurrency_count, |
| &disable_concurrency_count); |
| |
| // check if lpi should be used |
| use_lpi_ = rm->getLPIUsage(); |
| |
| /* |
| * When voice/voip/record is active and concurrency is not |
| * supported, mark paused as true, so that start recognition |
| * will be skipped and when voice/voip/record stops, stream |
| * will be resumed. |
| */ |
| if (disable_concurrency_count) { |
| paused_ = true; |
| } |
| |
| timer_thread_ = std::thread(TimerThread, std::ref(*this)); |
| timer_stop_waiting_ = false; |
| exit_timer_thread_ = false; |
| |
| PAL_DBG(LOG_TAG, "Exit"); |
| } |
| |
| StreamSoundTrigger::~StreamSoundTrigger() { |
| mStreamMutex.lock(); |
| { |
| std::lock_guard<std::mutex> lck(timer_mutex_); |
| exit_timer_thread_ = true; |
| timer_stop_waiting_ = true; |
| timer_wait_cond_.notify_one(); |
| timer_start_cond_.notify_one(); |
| } |
| if (timer_thread_.joinable()) { |
| PAL_DBG(LOG_TAG, "Join timer thread"); |
| timer_thread_.join(); |
| } |
| |
| st_states_.clear(); |
| engines_.clear(); |
| mStreamMutex.unlock(); |
| |
| rm->deregisterStream(this); |
| if (mStreamAttr) |
| free(mStreamAttr); |
| |
| if (gsl_engine_model_) |
| free(gsl_engine_model_); |
| |
| if (gsl_conf_levels_) |
| free(gsl_conf_levels_); |
| |
| if (mVolumeData) |
| free(mVolumeData); |
| |
| if (sm_config_) { |
| free(sm_config_); |
| sm_config_ = nullptr; |
| } |
| |
| if (rec_config_) { |
| free(rec_config_); |
| rec_config_ = nullptr; |
| } |
| |
| if (reader_) { |
| delete reader_; |
| reader_ = nullptr; |
| } |
| |
| if (st_conf_levels_) { |
| free(st_conf_levels_); |
| st_conf_levels_ = nullptr; |
| } |
| if (st_conf_levels_v2_) { |
| free(st_conf_levels_v2_); |
| st_conf_levels_v2_ = nullptr; |
| } |
| |
| if (st_idle_) |
| delete st_idle_; |
| if (st_loaded_) |
| delete st_loaded_; |
| if (st_active) |
| delete st_active; |
| if (st_detected_) |
| delete st_detected_; |
| if (st_buffering_) |
| delete st_buffering_; |
| if (st_ssr_) |
| delete st_ssr_; |
| |
| mDevices.clear(); |
| PAL_DBG(LOG_TAG, "Exit"); |
| } |
| |
| int32_t StreamSoundTrigger::close() { |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "Enter, stream direction %d", mStreamAttr->direction); |
| |
| std::lock_guard<std::mutex> lck(mStreamMutex); |
| std::shared_ptr<StEventConfig> ev_cfg(new StUnloadEventConfig()); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| |
| if (sm_config_) { |
| free(sm_config_); |
| sm_config_ = nullptr; |
| } |
| |
| currentState = STREAM_IDLE; |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::start() { |
| int32_t status = 0; |
| stream_state_t prev_state; |
| |
| /* |
| * Guard with mActiveStreamMutex to avoid concurrent |
| * RX stream getting released during EC enable |
| */ |
| rm->lockActiveStream(); |
| PAL_DBG(LOG_TAG, "Enter, stream direction %d", mStreamAttr->direction); |
| |
| std::lock_guard<std::mutex> lck(mStreamMutex); |
| // cache current state after mutex locked |
| prev_state = currentState; |
| currentState = STREAM_STARTED; |
| |
| rejection_notified_ = false; |
| std::shared_ptr<StEventConfig> ev_cfg( |
| new StStartRecognitionEventConfig(false)); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| // restore cached state if start fails |
| if (status) |
| currentState = prev_state; |
| |
| rm->unlockActiveStream(); |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::stop() { |
| int32_t status = 0; |
| |
| /* |
| * Guard with mActiveStreamMutex to avoid concurrent |
| * RX stream getting released during EC disable |
| */ |
| rm->lockActiveStream(); |
| PAL_DBG(LOG_TAG, "Enter, stream direction %d", mStreamAttr->direction); |
| |
| std::lock_guard<std::mutex> lck(mStreamMutex); |
| currentState = STREAM_STOPPED; |
| |
| std::shared_ptr<StEventConfig> ev_cfg( |
| new StStopRecognitionEventConfig(false)); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| |
| rm->unlockActiveStream(); |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::read(struct pal_buffer* buf) { |
| int32_t size = 0; |
| uint32_t sleep_ms = 0; |
| uint32_t offset = 0; |
| |
| PAL_VERBOSE(LOG_TAG, "Enter"); |
| |
| if (!buf || !buf->buffer) { |
| PAL_ERR(LOG_TAG, "Invalid buffer"); |
| return -EINVAL; |
| } |
| |
| std::lock_guard<std::mutex> lck(mStreamMutex); |
| if (cur_state_ == st_buffering_) { |
| if (!this->force_nlpi_vote) { |
| rm->voteSleepMonitor(this, true, true); |
| this->force_nlpi_vote = true; |
| |
| offset = vui_intf_->GetReadOffset(); |
| if (offset) { |
| reader_->advanceReadOffset(offset); |
| vui_intf_->SetReadOffset(0); |
| } |
| } |
| } else { |
| PAL_ERR(LOG_TAG, "Invalid Stream Current State %d", GetCurrentStateId()); |
| return size; |
| } |
| if (vui_ptfm_info_->GetEnableDebugDumps() && !lab_fd_) { |
| ST_DBG_FILE_OPEN_WR(lab_fd_, ST_DEBUG_DUMP_LOCATION, |
| "lab_reading", "bin", lab_cnt); |
| PAL_DBG(LOG_TAG, "lab data stored in: lab_reading_%d.bin", |
| lab_cnt); |
| lab_cnt++; |
| } |
| |
| std::shared_ptr<StEventConfig> ev_cfg( |
| new StReadBufferEventConfig((void *)buf)); |
| size = cur_state_->ProcessEvent(ev_cfg); |
| |
| vui_intf_->ProcessLab(buf->buffer, size); |
| |
| /* |
| * st stream read pcm data from ringbuffer with almost no |
| * delay, sleep for some time after each read even if read |
| * fails or no enough data in ring buffer |
| */ |
| if (size <= 0 || reader_->getUnreadSize() < buf->size) { |
| sleep_ms = (buf->size * BITS_PER_BYTE * MS_PER_SEC) / |
| (sm_cfg_->GetSampleRate() * sm_cfg_->GetBitWidth() * |
| sm_cfg_->GetOutChannels()); |
| std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); |
| } |
| |
| PAL_VERBOSE(LOG_TAG, "Exit, read size %d", size); |
| |
| return size; |
| } |
| |
| int32_t StreamSoundTrigger::getParameters(uint32_t param_id, void **payload) { |
| int32_t status = 0; |
| int32_t ret = 0; |
| struct pal_stream_attributes *sAttr = nullptr; |
| pal_param_payload *pal_payload = nullptr; |
| |
| PAL_DBG(LOG_TAG, "Enter, get parameter %u", param_id); |
| if (param_id == PAL_PARAM_ID_STREAM_ATTRIBUTES) { |
| pal_payload = (pal_param_payload *)(*payload); |
| if (pal_payload->payload_size != sizeof(struct pal_stream_attributes)) { |
| PAL_ERR(LOG_TAG, "Invalid payload size %u", pal_payload->payload_size); |
| return -EINVAL; |
| } |
| sAttr = (struct pal_stream_attributes *)(pal_payload->payload); |
| status = getStreamAttributes(sAttr); |
| if (status) |
| PAL_ERR(LOG_TAG, "Failed to get stream attributes"); |
| } else if (param_id == PAL_PARAM_ID_WAKEUP_MODULE_VERSION) { |
| std::vector<std::shared_ptr<VUIStreamConfig>> sm_cfg_list; |
| |
| vui_ptfm_info_->GetStreamConfigForVersionQuery(sm_cfg_list); |
| if (sm_cfg_list.size() == 0) { |
| PAL_ERR(LOG_TAG, "No sound model config supports version query"); |
| return -EINVAL; |
| } |
| |
| sm_cfg_ = sm_cfg_list[0]; |
| if (!sm_cfg_) { |
| PAL_ERR(LOG_TAG, "Failed to get sound model config"); |
| return -EINVAL; |
| } |
| |
| if (!mDevices.size()) { |
| std::shared_ptr<Device> dev = nullptr; |
| |
| // update best device |
| pal_device_id_t dev_id = GetAvailCaptureDevice(); |
| PAL_DBG(LOG_TAG, "Select available caputre device %d", dev_id); |
| |
| dev = GetPalDevice(this, dev_id); |
| if (!dev) { |
| PAL_ERR(LOG_TAG, "Device creation is failed"); |
| return -EINVAL; |
| } |
| mDevices.push_back(dev); |
| dev = nullptr; |
| } |
| |
| if (mDevices.size() > 0 && !device_opened_) { |
| status = mDevices[0]->open(); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Device open failed, status %d", status); |
| return status; |
| } |
| device_opened_ = true; |
| } |
| |
| cap_prof_ = GetCurrentCaptureProfile(); |
| /* |
| * Get the capture profile and module types to fill selectors |
| * used in payload builder to retrieve stream and device PP GKVs |
| */ |
| mDevPPSelector = cap_prof_->GetName(); |
| PAL_DBG(LOG_TAG, "Devicepp Selector: %s", mDevPPSelector.c_str()); |
| mStreamSelector = sm_cfg_->GetVUIModuleName(); |
| |
| model_type_ = sm_cfg_->GetVUIModuleType(); |
| PAL_DBG(LOG_TAG, "Module Type:%d, Name: %s", model_type_, mStreamSelector.c_str()); |
| mInstanceID = rm->getStreamInstanceID(this); |
| |
| gsl_engine_ = SoundTriggerEngine::Create(this, ST_SM_ID_SVA_F_STAGE_GMM, |
| model_type_, sm_cfg_); |
| if (!gsl_engine_) { |
| PAL_ERR(LOG_TAG, "big_sm: gsl engine creation failed"); |
| status = -ENOMEM; |
| goto release; |
| } |
| |
| status = gsl_engine_->GetParameters(param_id, payload); |
| if (status) |
| PAL_ERR(LOG_TAG, "Failed to get parameters from engine %d", status); |
| |
| release: |
| rm->resetStreamInstanceID(this, mInstanceID); |
| if (mDevices.size() > 0) { |
| ret = mDevices[0]->close(); |
| device_opened_ = false; |
| if (0 != ret) { |
| PAL_ERR(LOG_TAG, "Device close failed, status %d", ret); |
| status = ret; |
| } |
| } |
| } else if (gsl_engine_) { |
| status = gsl_engine_->GetParameters(param_id, payload); |
| if (status) |
| PAL_ERR(LOG_TAG, "Failed to get parameters from engine, status %d", status); |
| } else { |
| PAL_ERR(LOG_TAG, "No gsl engine present"); |
| status = -EINVAL; |
| } |
| |
| PAL_DBG(LOG_TAG, "Exit status: %d", status); |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::setParameters(uint32_t param_id, void *payload) { |
| int32_t status = 0; |
| pal_param_payload *param_payload = (pal_param_payload *)payload; |
| struct pal_st_recognition_config *new_rec_config = nullptr; |
| |
| if (param_id != PAL_PARAM_ID_STOP_BUFFERING && !param_payload) { |
| PAL_ERR(LOG_TAG, "Invalid payload for param ID: %d", param_id); |
| return -EINVAL; |
| } |
| |
| PAL_DBG(LOG_TAG, "Enter, param id %d", param_id); |
| |
| std::lock_guard<std::mutex> lck(mStreamMutex); |
| switch (param_id) { |
| case PAL_PARAM_ID_LOAD_SOUND_MODEL: { |
| std::shared_ptr<StEventConfig> ev_cfg( |
| new StLoadEventConfig((void *)param_payload->payload)); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| if (!status) |
| currentState = STREAM_OPENED; |
| break; |
| } |
| case PAL_PARAM_ID_RECOGNITION_CONFIG: { |
| new_rec_config = |
| (struct pal_st_recognition_config *)param_payload->payload; |
| std::shared_ptr<StEventConfig> ev_cfg( |
| new StRecognitionCfgEventConfig((void *)new_rec_config)); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| break; |
| } |
| case PAL_PARAM_ID_STOP_BUFFERING: { |
| std::shared_ptr<StEventConfig> ev_cfg( |
| new StStopBufferingEventConfig()); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| |
| if (vui_ptfm_info_->GetEnableDebugDumps()) { |
| ST_DBG_FILE_CLOSE(lab_fd_); |
| lab_fd_ = nullptr; |
| } |
| break; |
| } |
| default: { |
| status = -EINVAL; |
| PAL_ERR(LOG_TAG, "Unsupported param %u", param_id); |
| break; |
| } |
| } |
| |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::HandleConcurrentStream(bool active) { |
| int32_t status = 0; |
| uint64_t transit_duration = 0; |
| |
| if (!active) { |
| mStreamMutex.lock(); |
| transit_start_time_ = std::chrono::steady_clock::now(); |
| common_cp_update_disable_ = true; |
| } |
| |
| PAL_DBG(LOG_TAG, "Enter"); |
| std::shared_ptr<StEventConfig> ev_cfg( |
| new StConcurrentStreamEventConfig(active)); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| |
| if (active) { |
| transit_end_time_ = std::chrono::steady_clock::now(); |
| transit_duration = |
| std::chrono::duration_cast<std::chrono::milliseconds>( |
| transit_end_time_ - transit_start_time_).count(); |
| common_cp_update_disable_ = false; |
| if (use_lpi_) { |
| PAL_INFO(LOG_TAG, "NLPI->LPI switch takes %llums", |
| (long long)transit_duration); |
| } else { |
| PAL_INFO(LOG_TAG, "LPI->NLPI switch takes %llums", |
| (long long)transit_duration); |
| } |
| mStreamMutex.unlock(); |
| } |
| |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::EnableLPI(bool is_enable) { |
| std::lock_guard<std::mutex> lck(mStreamMutex); |
| if (!rm->IsLPISupported(PAL_STREAM_VOICE_UI)) { |
| PAL_DBG(LOG_TAG, "Ignore as LPI not supported"); |
| } else { |
| use_lpi_ = is_enable; |
| } |
| |
| return 0; |
| } |
| |
| int32_t StreamSoundTrigger::setECRef(std::shared_ptr<Device> dev, bool is_enable) { |
| int32_t status = 0; |
| |
| std::lock_guard<std::mutex> lck(mStreamMutex); |
| if (use_lpi_) { |
| PAL_DBG(LOG_TAG, "EC ref will be handled in LPI/NLPI switch"); |
| return status; |
| } |
| // check if SVA device is registered in rm to avoid unnecessary EC set |
| if (is_enable) { |
| if (mDevices.size() && rm->isDeviceActive_l(mDevices[0], this)) |
| status = setECRef_l(dev, is_enable); |
| else |
| status = -ENODEV; |
| } else { |
| status = setECRef_l(dev, is_enable); |
| } |
| |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::setECRef_l(std::shared_ptr<Device> dev, bool is_enable) { |
| int32_t status = 0; |
| std::shared_ptr<StEventConfig> ev_cfg( |
| new StECRefEventConfig(dev, is_enable)); |
| |
| PAL_DBG(LOG_TAG, "Enter, enable %d", is_enable); |
| |
| if (!cap_prof_ || !cap_prof_->isECRequired()) { |
| PAL_DBG(LOG_TAG, "No need to set ec ref"); |
| goto exit; |
| } |
| |
| if (dev && !rm->checkECRef(dev, mDevices[0])) { |
| PAL_DBG(LOG_TAG, "No need to set ec ref for unmatching rx device"); |
| goto exit; |
| } |
| |
| status = cur_state_->ProcessEvent(ev_cfg); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to handle ec ref event"); |
| goto exit; |
| } |
| |
| if (is_enable) { |
| ec_rx_dev_ = dev; |
| } else { |
| ec_rx_dev_ = nullptr; |
| } |
| |
| exit: |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::DisconnectDevice(pal_device_id_t device_id) { |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "Enter"); |
| /* |
| * NOTE: mStreamMutex will be unlocked after ConnectDevice handled |
| * because device disconnect/connect should be handled sequencely, |
| * and no other commands from client should be handled between |
| * device disconnect and connect. |
| */ |
| mStreamMutex.lock(); |
| std::shared_ptr<StEventConfig> ev_cfg( |
| new StDeviceDisconnectedEventConfig(device_id)); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to disconnect device %d", device_id); |
| } |
| |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::ConnectDevice(pal_device_id_t device_id) { |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "Enter"); |
| std::shared_ptr<StEventConfig> ev_cfg( |
| new StDeviceConnectedEventConfig(device_id)); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to connect device %d", device_id); |
| } |
| mStreamMutex.unlock(); |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::Resume() { |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "Enter"); |
| std::lock_guard<std::mutex> lck(mStreamMutex); |
| std::shared_ptr<StEventConfig> ev_cfg(new StResumeEventConfig()); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Resume failed"); |
| } |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::Pause() { |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "Enter"); |
| std::lock_guard<std::mutex> lck(mStreamMutex); |
| std::shared_ptr<StEventConfig> ev_cfg(new StPauseEventConfig()); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Pause failed"); |
| } |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::isSampleRateSupported(uint32_t sampleRate) { |
| int32_t rc = 0; |
| |
| PAL_DBG(LOG_TAG, "sampleRate %u", sampleRate); |
| switch (sampleRate) { |
| case SAMPLINGRATE_8K: |
| case SAMPLINGRATE_16K: |
| case SAMPLINGRATE_32K: |
| case SAMPLINGRATE_44K: |
| case SAMPLINGRATE_48K: |
| case SAMPLINGRATE_96K: |
| case SAMPLINGRATE_192K: |
| case SAMPLINGRATE_384K: |
| break; |
| default: |
| rc = -EINVAL; |
| PAL_ERR(LOG_TAG, "sample rate not supported rc %d", rc); |
| break; |
| } |
| |
| return rc; |
| } |
| |
| int32_t StreamSoundTrigger::isChannelSupported(uint32_t numChannels) { |
| int32_t rc = 0; |
| |
| PAL_DBG(LOG_TAG, "numChannels %u", numChannels); |
| switch (numChannels) { |
| case CHANNELS_1: |
| case CHANNELS_2: |
| case CHANNELS_3: |
| case CHANNELS_4: |
| case CHANNELS_5: |
| case CHANNELS_5_1: |
| case CHANNELS_7: |
| case CHANNELS_8: |
| break; |
| default: |
| rc = -EINVAL; |
| PAL_ERR(LOG_TAG, "channels not supported rc %d", rc); |
| break; |
| } |
| return rc; |
| } |
| |
| int32_t StreamSoundTrigger::isBitWidthSupported(uint32_t bitWidth) { |
| int32_t rc = 0; |
| |
| PAL_DBG(LOG_TAG, "bitWidth %u", bitWidth); |
| switch (bitWidth) { |
| case BITWIDTH_16: |
| case BITWIDTH_24: |
| case BITWIDTH_32: |
| break; |
| default: |
| rc = -EINVAL; |
| PAL_ERR(LOG_TAG, "bit width not supported rc %d", rc); |
| break; |
| } |
| return rc; |
| } |
| |
| int32_t StreamSoundTrigger::registerCallBack(pal_stream_callback cb, |
| uint64_t cookie) { |
| callback_ = cb; |
| cookie_ = cookie; |
| |
| PAL_VERBOSE(LOG_TAG, "callback_ = %pK", callback_); |
| |
| return 0; |
| } |
| |
| int32_t StreamSoundTrigger::getCallBack(pal_stream_callback *cb) { |
| if (!cb) { |
| PAL_ERR(LOG_TAG, "Invalid cb"); |
| return -EINVAL; |
| } |
| // Do not expect this to be called. |
| *cb = callback_; |
| return 0; |
| } |
| |
| int32_t StreamSoundTrigger::SetEngineDetectionState(int32_t det_type) { |
| int32_t status = 0; |
| bool lock_status = false; |
| |
| PAL_DBG(LOG_TAG, "Enter, det_type %d", det_type); |
| if (!(det_type & DETECTION_TYPE_ALL)) { |
| PAL_ERR(LOG_TAG, "Invalid detection type %d", det_type); |
| return -EINVAL; |
| } |
| |
| /* |
| * setEngineDetectionState should only be called when stream |
| * is in ACTIVE state(for first stage) or in BUFFERING state |
| * (for second stage) |
| */ |
| do { |
| lock_status = mStreamMutex.try_lock(); |
| } while (!lock_status && (GetCurrentStateId() == ST_STATE_ACTIVE || |
| GetCurrentStateId() == ST_STATE_BUFFERING)); |
| |
| if ((det_type == GMM_DETECTED && |
| GetCurrentStateId() != ST_STATE_ACTIVE) || |
| ((det_type & DETECTION_TYPE_SS) && |
| GetCurrentStateId() != ST_STATE_BUFFERING)) { |
| if (lock_status) |
| mStreamMutex.unlock(); |
| PAL_DBG(LOG_TAG, "Exit as stream not in proper state"); |
| return -EINVAL; |
| } |
| |
| if (det_type == GMM_DETECTED) { |
| rm->acquireWakeLock(); |
| reader_->updateState(READER_ENABLED); |
| } |
| |
| std::shared_ptr<StEventConfig> ev_cfg( |
| new StDetectedEventConfig(det_type)); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| |
| /* |
| * mStreamMutex may get unlocked in handling detection event |
| * and not locked back when stream gets stopped/unloaded, |
| * when this happens, mutex_unlocked_after_cb_ will be set to |
| * true, so check mutex_unlocked_after_cb_ here to avoid |
| * double unlock. |
| */ |
| if (!mutex_unlocked_after_cb_) |
| mStreamMutex.unlock(); |
| else |
| mutex_unlocked_after_cb_ = false; |
| |
| if (det_type == USER_VERIFICATION_REJECT || |
| det_type == KEYWORD_DETECTION_REJECT) |
| rm->handleDeferredSwitch(); |
| |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| return status; |
| } |
| |
| void StreamSoundTrigger::InternalStopRecognition() { |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "Enter"); |
| std::lock_guard<std::mutex> lck(mStreamMutex); |
| if (pending_stop_) { |
| std::shared_ptr<StEventConfig> ev_cfg( |
| new StStopRecognitionEventConfig(true)); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| } |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| } |
| |
| void StreamSoundTrigger::TimerThread(StreamSoundTrigger& st_stream) { |
| PAL_DBG(LOG_TAG, "Enter"); |
| |
| std::unique_lock<std::mutex> lck(st_stream.timer_mutex_); |
| while (!st_stream.exit_timer_thread_) { |
| st_stream.timer_start_cond_.wait(lck); |
| if (st_stream.exit_timer_thread_) |
| break; |
| |
| st_stream.timer_wait_cond_.wait_for(lck, |
| std::chrono::milliseconds(ST_DEFERRED_STOP_DEALY_MS)); |
| |
| if (!st_stream.timer_stop_waiting_ && !st_stream.exit_timer_thread_) { |
| st_stream.timer_mutex_.unlock(); |
| st_stream.InternalStopRecognition(); |
| st_stream.timer_mutex_.lock(); |
| } |
| } |
| PAL_DBG(LOG_TAG, "Exit"); |
| } |
| |
| void StreamSoundTrigger::PostDelayedStop() { |
| PAL_VERBOSE(LOG_TAG, "Post Delayed Stop for %p", this); |
| pending_stop_ = true; |
| std::lock_guard<std::mutex> lck(timer_mutex_); |
| timer_stop_waiting_ = false; |
| timer_start_cond_.notify_one(); |
| } |
| |
| void StreamSoundTrigger::CancelDelayedStop() { |
| PAL_VERBOSE(LOG_TAG, "Cancel Delayed stop for %p", this); |
| pending_stop_ = false; |
| std::lock_guard<std::mutex> lck(timer_mutex_); |
| timer_stop_waiting_ = true; |
| timer_wait_cond_.notify_one(); |
| } |
| |
| std::shared_ptr<SoundTriggerEngine> StreamSoundTrigger::HandleEngineLoad( |
| uint8_t *sm_data, |
| int32_t sm_size, |
| listen_model_indicator_enum type, |
| st_module_type_t module_type) { |
| |
| int status = 0; |
| std::shared_ptr<SoundTriggerEngine> engine = nullptr; |
| |
| engine = SoundTriggerEngine::Create(this, type, module_type, sm_cfg_); |
| if (!engine) { |
| status = -ENOMEM; |
| PAL_ERR(LOG_TAG, "engine creation failed for type %u", type); |
| goto error_exit; |
| } |
| |
| // cache 1st stage model for concurrency handling |
| if (type == ST_SM_ID_SVA_F_STAGE_GMM) { |
| gsl_engine_model_ = (uint8_t *)realloc(gsl_engine_model_, sm_size); |
| if (!gsl_engine_model_) { |
| PAL_ERR(LOG_TAG, "Failed to allocate memory for gsl model"); |
| goto error_exit; |
| } |
| ar_mem_cpy(gsl_engine_model_, sm_size, sm_data, sm_size); |
| gsl_engine_model_size_ = sm_size; |
| // Create Voice UI Interface object and update to engines |
| vui_intf_ = engine->GetVoiceUIInterface(); |
| if (!vui_intf_) { |
| vui_intf_ = VoiceUIInterface::Create(sm_cfg_); |
| if (!vui_intf_) { |
| PAL_ERR(LOG_TAG, "Failed to create interface, status %d", |
| status); |
| goto error_exit; |
| } |
| engine->SetVoiceUIInterface(vui_intf_); |
| vui_intf_->SetSTModuleType(module_type); |
| } |
| |
| // register stream/model to Voice UI interface |
| status = vui_intf_->RegisterModel(this, |
| sm_config_, gsl_engine_model_, gsl_engine_model_size_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to register stream/model, status %d", |
| status); |
| goto error_exit; |
| } |
| UpdateModelId(module_type); |
| vui_intf_->SetModelId(this, model_id_); |
| vui_intf_->SetRecognitionMode(this, recognition_mode_); |
| } |
| |
| status = engine->LoadSoundModel(this, sm_data, sm_size); |
| if (status) { |
| PAL_ERR(LOG_TAG, "big_sm: gsl engine loading model" |
| "failed, status %d", status); |
| goto error_exit; |
| } |
| |
| return engine; |
| |
| error_exit: |
| if (gsl_engine_model_) |
| free(gsl_engine_model_); |
| if (vui_intf_) |
| vui_intf_->DeregisterModel(this); |
| |
| return nullptr; |
| } |
| |
| /* |
| * Return stream instance id for gkv popluation |
| * For PDK: always return INSTANCE_1 if only single instance of first stage |
| sound model is allowed, otherwise return respective instance ID |
| till total number of allowed instances. |
| * For SVA4: just return stream instance id |
| */ |
| uint32_t StreamSoundTrigger::GetInstanceId() { |
| if ((IS_MODULE_TYPE_PDK(model_type_) && |
| sm_cfg_->isSingleInstanceStage1()) || |
| (model_type_ == ST_MODULE_TYPE_GMM && |
| sm_cfg_->GetMergeFirstStageSoundModels())) |
| return INSTANCE_1; |
| else if (IS_MODULE_TYPE_PDK(model_type_)) |
| return mInstanceID < sm_cfg_->GetSupportedEngineCount() ? |
| mInstanceID : sm_cfg_->GetSupportedEngineCount(); |
| else |
| return mInstanceID; |
| } |
| |
| void StreamSoundTrigger::GetUUID(class SoundTriggerUUID *uuid, |
| struct pal_st_sound_model *sound_model) { |
| |
| uuid->timeLow = (uint32_t)sound_model->vendor_uuid.timeLow; |
| uuid->timeMid = (uint16_t)sound_model->vendor_uuid.timeMid; |
| uuid->timeHiAndVersion = (uint16_t)sound_model->vendor_uuid.timeHiAndVersion; |
| uuid->clockSeq = (uint16_t)sound_model->vendor_uuid.clockSeq; |
| uuid->node[0] = (uint8_t)sound_model->vendor_uuid.node[0]; |
| uuid->node[1] = (uint8_t)sound_model->vendor_uuid.node[1]; |
| uuid->node[2] = (uint8_t)sound_model->vendor_uuid.node[2]; |
| uuid->node[3] = (uint8_t)sound_model->vendor_uuid.node[3]; |
| uuid->node[4] = (uint8_t)sound_model->vendor_uuid.node[4]; |
| uuid->node[5] = (uint8_t)sound_model->vendor_uuid.node[5]; |
| } |
| |
| void StreamSoundTrigger::updateStreamAttributes() { |
| |
| /* |
| * In case of Single mic handset/headset use cases, stream channels > 1 |
| * is not a valid configuration. Override the stream attribute channels if the |
| * device channels is set to 1 |
| */ |
| if (mStreamAttr) { |
| if (cap_prof_->GetChannels() == CHANNELS_1 && |
| sm_cfg_->GetOutChannels() > CHANNELS_1) { |
| mStreamAttr->in_media_config.ch_info.channels = CHANNELS_1; |
| } else { |
| mStreamAttr->in_media_config.ch_info.channels = |
| sm_cfg_->GetOutChannels(); |
| } |
| /* Update channel map in stream attributes to be in sync with channels */ |
| switch (mStreamAttr->in_media_config.ch_info.channels) { |
| case CHANNELS_2: |
| mStreamAttr->in_media_config.ch_info.ch_map[0] = |
| PAL_CHMAP_CHANNEL_FL; |
| mStreamAttr->in_media_config.ch_info.ch_map[1] = |
| PAL_CHMAP_CHANNEL_FR; |
| break; |
| case CHANNELS_1: |
| default: |
| mStreamAttr->in_media_config.ch_info.ch_map[0] = |
| PAL_CHMAP_CHANNEL_FL; |
| break; |
| } |
| |
| mStreamAttr->in_media_config.sample_rate = |
| sm_cfg_->GetSampleRate(); |
| mStreamAttr->in_media_config.bit_width = |
| sm_cfg_->GetBitWidth(); |
| } |
| } |
| |
| void StreamSoundTrigger::UpdateModelId(st_module_type_t type) { |
| if (IS_MODULE_TYPE_PDK(type) && mInstanceID) |
| model_id_ = ((uint32_t)type << ST_MODEL_TYPE_SHIFT) + mInstanceID; |
| } |
| |
| /* TODO: |
| * - Need to track vendor UUID |
| */ |
| int32_t StreamSoundTrigger::LoadSoundModel( |
| struct pal_st_sound_model *sound_model) { |
| |
| int32_t status = 0; |
| int32_t engine_id = 0; |
| std::shared_ptr<SoundTriggerEngine> engine = nullptr; |
| std::shared_ptr<EngineCfg> engine_cfg = nullptr; |
| std::vector<sm_pair_t> model_list; |
| |
| PAL_DBG(LOG_TAG, "Enter"); |
| |
| status = UpdateSoundModel(sound_model); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to update sound model, status %d", status); |
| goto error_exit; |
| } |
| |
| /* Update stream attributes as per sound model config */ |
| updateStreamAttributes(); |
| |
| // Parse sound model with Voice UI interface |
| status = VoiceUIInterface::ParseSoundModel(sm_cfg_, |
| sound_model, model_type_, model_list); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to parse sound model, status %d", status); |
| goto error_exit; |
| } |
| |
| if (!sm_cfg_->isQCVAUUID()) { |
| mStreamSelector = sm_cfg_->GetVUIModuleName(); |
| } else { |
| mStreamSelector = sm_cfg_->GetVUIModuleName(model_type_); |
| } |
| mInstanceID = rm->getStreamInstanceID(this); |
| |
| // Create engines by parsing result |
| for (auto iter: model_list) { |
| engine_id = static_cast<int32_t>(iter.first); |
| engine = HandleEngineLoad((uint8_t *)iter.second.first, |
| iter.second.second, |
| iter.first, model_type_); |
| if (!engine) { |
| PAL_ERR(LOG_TAG, "Failed to create engine"); |
| status = -EINVAL; |
| goto error_exit; |
| } |
| std::shared_ptr<EngineCfg> engine_cfg(new EngineCfg( |
| engine_id, engine, (void *)iter.second.first, iter.second.second)); |
| |
| AddEngine(engine_cfg); |
| if (iter.first == ST_SM_ID_SVA_F_STAGE_GMM) { |
| gsl_engine_ = engine; |
| } else { |
| if (iter.first & ST_SM_ID_SVA_S_STAGE_KWD) { |
| notification_state_ |= KEYWORD_DETECTION_SUCCESS; |
| } else if (iter.first == ST_SM_ID_SVA_S_STAGE_USER) { |
| notification_state_ |= USER_VERIFICATION_SUCCESS; |
| } |
| } |
| } |
| |
| // update voice ui interface for second stage engines |
| for (auto &eng: engines_) { |
| if (eng->GetEngineId() & ST_SM_ID_SVA_S_STAGE_KWD || |
| eng->GetEngineId() == ST_SM_ID_SVA_S_STAGE_USER) |
| eng->GetEngine()->SetVoiceUIInterface(vui_intf_); |
| } |
| |
| goto exit; |
| |
| error_exit: |
| for (auto iter: model_list) { |
| if (iter.second.first) |
| free(iter.second.first); |
| } |
| for (auto &eng: engines_) { |
| eng->GetEngine()->UnloadSoundModel(this); |
| } |
| engines_.clear(); |
| gsl_engine_.reset(); |
| rm->resetStreamInstanceID(this, mInstanceID); |
| if (sm_config_) { |
| free(sm_config_); |
| sm_config_ = nullptr; |
| } |
| exit: |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::UpdateSoundModel( |
| struct pal_st_sound_model *sound_model) { |
| int32_t status = 0; |
| int32_t sm_size = 0; |
| struct pal_st_phrase_sound_model *phrase_sm = nullptr; |
| struct pal_st_sound_model *common_sm = nullptr; |
| class SoundTriggerUUID uuid; |
| |
| PAL_DBG(LOG_TAG, "Enter"); |
| |
| if (!sound_model) { |
| PAL_ERR(LOG_TAG, "Invalid sound_model param status %d", status); |
| status = -EINVAL; |
| goto exit; |
| } |
| sound_model_type_ = sound_model->type; |
| |
| if (sound_model->type == PAL_SOUND_MODEL_TYPE_KEYPHRASE) { |
| phrase_sm = (struct pal_st_phrase_sound_model *)sound_model; |
| if ((phrase_sm->common.data_offset < sizeof(*phrase_sm)) || |
| (phrase_sm->common.data_size == 0) || |
| (phrase_sm->num_phrases == 0)) { |
| PAL_ERR(LOG_TAG, "Invalid phrase sound model params data size=%d, " |
| "data offset=%d, type=%d phrases=%d status %d", |
| phrase_sm->common.data_size, phrase_sm->common.data_offset, |
| phrase_sm->common.type,phrase_sm->num_phrases, status); |
| status = -EINVAL; |
| goto exit; |
| } |
| common_sm = (struct pal_st_sound_model*)&phrase_sm->common; |
| sm_size = sizeof(*phrase_sm) + common_sm->data_size; |
| |
| } else if (sound_model->type == PAL_SOUND_MODEL_TYPE_GENERIC) { |
| if ((sound_model->data_size == 0) || |
| (sound_model->data_offset < sizeof(struct pal_st_sound_model))) { |
| PAL_ERR(LOG_TAG, "Invalid generic sound model params data size=%d," |
| " data offset=%d status %d", sound_model->data_size, |
| sound_model->data_offset, status); |
| status = -EINVAL; |
| goto exit; |
| } |
| common_sm = sound_model; |
| sm_size = sizeof(*common_sm) + common_sm->data_size; |
| } else { |
| PAL_ERR(LOG_TAG, "Unknown sound model type - %d status %d", |
| sound_model->type, status); |
| status = -EINVAL; |
| goto exit; |
| } |
| if (sm_config_ != sound_model) { |
| // Cache to use during SSR and other internal events handling. |
| if (sm_config_) { |
| free(sm_config_); |
| } |
| sm_config_ = (struct pal_st_sound_model *)calloc(1, sm_size); |
| if (!sm_config_) { |
| PAL_ERR(LOG_TAG, "sound model config allocation failed, status %d", |
| status); |
| status = -ENOMEM; |
| goto exit; |
| } |
| |
| if (sound_model->type == PAL_SOUND_MODEL_TYPE_KEYPHRASE) { |
| ar_mem_cpy(sm_config_, sizeof(*phrase_sm), |
| phrase_sm, sizeof(*phrase_sm)); |
| ar_mem_cpy((uint8_t *)sm_config_ + common_sm->data_offset, |
| common_sm->data_size, |
| (uint8_t *)phrase_sm + common_sm->data_offset, |
| common_sm->data_size); |
| recognition_mode_ = phrase_sm->phrases[0].recognition_mode; |
| } else { |
| ar_mem_cpy(sm_config_, sizeof(*common_sm), |
| common_sm, sizeof(*common_sm)); |
| ar_mem_cpy((uint8_t *)sm_config_ + common_sm->data_offset, |
| common_sm->data_size, |
| (uint8_t *)common_sm + common_sm->data_offset, |
| common_sm->data_size); |
| recognition_mode_ = PAL_RECOGNITION_MODE_VOICE_TRIGGER; |
| } |
| } |
| GetUUID(&uuid, sound_model); |
| this->sm_cfg_ = this->vui_ptfm_info_->GetStreamConfig(uuid); |
| if (!this->sm_cfg_) { |
| PAL_ERR(LOG_TAG, "Failed to get sound model config"); |
| status = -EINVAL; |
| } |
| exit: |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| return status; |
| } |
| |
| // TODO: look into how cookies are used here |
| int32_t StreamSoundTrigger::SendRecognitionConfig( |
| struct pal_st_recognition_config *config) { |
| |
| int32_t status = 0; |
| int32_t i = 0; |
| uint32_t hist_buffer_duration = 0; |
| uint32_t pre_roll_duration = 0; |
| uint32_t client_capture_read_delay = 0; |
| uint8_t *conf_levels = NULL; |
| uint32_t num_conf_levels = 0; |
| uint32_t ring_buffer_len = 0; |
| uint32_t ring_buffer_size = 0; |
| uint32_t sec_stage_threshold = 0; |
| |
| PAL_DBG(LOG_TAG, "Enter"); |
| if (!vui_intf_) { |
| PAL_ERR(LOG_TAG, "VoiceUI Interface not created!"); |
| return -EINVAL; |
| } |
| |
| status = UpdateRecognitionConfig(config); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to update recognition config, status %d", |
| status); |
| goto error_exit; |
| } |
| |
| // dump recognition config opaque data |
| if (config->data_size > 0 && vui_ptfm_info_->GetEnableDebugDumps()) { |
| ST_DBG_DECLARE(FILE *rec_opaque_fd = NULL; static int rec_opaque_cnt = 0); |
| ST_DBG_FILE_OPEN_WR(rec_opaque_fd, ST_DEBUG_DUMP_LOCATION, |
| "rec_config_opaque", "bin", rec_opaque_cnt); |
| ST_DBG_FILE_WRITE(rec_opaque_fd, |
| (uint8_t *)rec_config_ + config->data_offset, config->data_size); |
| ST_DBG_FILE_CLOSE(rec_opaque_fd); |
| PAL_DBG(LOG_TAG, "recognition config opaque data stored in: rec_config_opaque_%d.bin", |
| rec_opaque_cnt); |
| rec_opaque_cnt++; |
| } |
| |
| // Parse recognition config with VoiceUI Interface |
| status = vui_intf_->ParseRecognitionConfig(this, rec_config_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to parse recognition config, status %d", |
| status); |
| goto error_exit; |
| } |
| |
| // acquire buffering config for current stream |
| vui_intf_->GetBufferingConfigs(this, &hist_buf_duration_, |
| &pre_roll_duration_); |
| |
| // use default value if preroll is not set |
| if (pre_roll_duration_ == 0) { |
| pre_roll_duration_ = sm_cfg_->GetPreRollDuration(); |
| } |
| |
| client_capture_read_delay = sm_cfg_->GetCaptureReadDelay(); |
| PAL_DBG(LOG_TAG, "history buf len = %d, preroll len = %d, read delay = %d", |
| hist_buf_duration_, pre_roll_duration_, client_capture_read_delay); |
| |
| if (!hist_buf_duration_) { |
| status = gsl_engine_->UpdateBufConfig(this, |
| sm_cfg_->GetKwDuration(), pre_roll_duration_); |
| } else { |
| status = gsl_engine_->UpdateBufConfig(this, |
| hist_buf_duration_, pre_roll_duration_); |
| } |
| |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to update buf config, status %d", status); |
| goto error_exit; |
| } |
| |
| /* |
| * Get the updated buffer config from engine as multiple streams |
| * attached to it might have different buffer configurations |
| */ |
| gsl_engine_->GetUpdatedBufConfig(&hist_buffer_duration, |
| &pre_roll_duration); |
| |
| PAL_INFO(LOG_TAG, "updated hist buf len = %d, preroll len = %d in gsl engine", |
| hist_buffer_duration, pre_roll_duration); |
| |
| // update input buffer size for mmap usecase |
| if (vui_ptfm_info_->GetMmapEnable()) { |
| inBufSize = vui_ptfm_info_->GetMmapFrameLength() * |
| sm_cfg_->GetSampleRate() * sm_cfg_->GetBitWidth() * |
| sm_cfg_->GetOutChannels() / (MS_PER_SEC * BITS_PER_BYTE); |
| if (!inBufSize) { |
| PAL_ERR(LOG_TAG, "Invalid frame size, use default value"); |
| inBufSize = BUF_SIZE_CAPTURE; |
| } |
| } |
| |
| // create ring buffer for lab transfer in gsl_engine |
| ring_buffer_len = hist_buffer_duration + pre_roll_duration + |
| client_capture_read_delay; |
| ring_buffer_size = (ring_buffer_len / MS_PER_SEC) * sm_cfg_->GetSampleRate() * |
| sm_cfg_->GetBitWidth() * |
| sm_cfg_->GetOutChannels() / BITS_PER_BYTE; |
| status = gsl_engine_->CreateBuffer(ring_buffer_size, |
| engines_.size(), reader_list_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to get ring buf reader, status %d", status); |
| goto error_exit; |
| } |
| |
| /* |
| * Assign created readers based on sound model sequence. |
| * For first stage engine, assign reader to stream side. |
| */ |
| for (i = 0; i < engines_.size(); i++) { |
| if (engines_[i]->GetEngineId() == ST_SM_ID_SVA_F_STAGE_GMM) { |
| reader_ = reader_list_[i]; |
| } else { |
| status = engines_[i]->GetEngine()->SetBufferReader( |
| reader_list_[i]); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to set ring buffer reader"); |
| goto error_exit; |
| } |
| } |
| } |
| |
| for (auto &eng: engines_) { |
| if (eng->GetEngineId() == ST_SM_ID_SVA_F_STAGE_GMM) { |
| vui_intf_->GetWakeupConfigs(this, (void **)&conf_levels, &num_conf_levels); |
| eng->GetEngine()->UpdateConfLevels(this, config, conf_levels, num_conf_levels); |
| if (num_conf_levels > 0) { |
| gsl_conf_levels_ = (uint8_t *)realloc(gsl_conf_levels_, |
| num_conf_levels); |
| if (!gsl_conf_levels_) { |
| PAL_ERR(LOG_TAG, "Failed to allocate gsl conf levels memory"); |
| status = -ENOMEM; |
| goto error_exit; |
| } |
| ar_mem_cpy(gsl_conf_levels_, |
| num_conf_levels, conf_levels, num_conf_levels); |
| gsl_conf_levels_size_ = num_conf_levels; |
| } |
| } else if (eng->GetEngineId() & ST_SM_ID_SVA_S_STAGE_KWD || |
| eng->GetEngineId() == ST_SM_ID_SVA_S_STAGE_USER) { |
| vui_intf_->GetSecondStageConfLevels(this, |
| (listen_model_indicator_enum)eng->GetEngineId(), |
| &sec_stage_threshold); |
| eng->GetEngine()->UpdateConfLevels(this, |
| config, (uint8_t *)&sec_stage_threshold, 1); |
| } |
| } |
| |
| // Update capture requested flag to gsl engine |
| if (!config->capture_requested && engines_.size() == 1) |
| capture_requested_ = false; |
| else |
| capture_requested_ = true; |
| gsl_engine_->SetCaptureRequested(capture_requested_); |
| goto exit; |
| |
| error_exit: |
| if (rec_config_) { |
| free(rec_config_); |
| rec_config_ = nullptr; |
| } |
| |
| exit: |
| |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::UpdateRecognitionConfig( |
| struct pal_st_recognition_config *config) { |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "Enter"); |
| if (!config) { |
| PAL_ERR(LOG_TAG, "Invalid config"); |
| status = -EINVAL; |
| goto exit; |
| } |
| if (rec_config_ != config) { |
| // Possible due to subsequent detections. |
| if (rec_config_) { |
| free(rec_config_); |
| } |
| rec_config_ = (struct pal_st_recognition_config *)calloc(1, |
| sizeof(struct pal_st_recognition_config) + config->data_size); |
| if (!rec_config_) { |
| PAL_ERR(LOG_TAG, "Failed to allocate rec_config status %d", status); |
| status = -ENOMEM; |
| goto exit; |
| } |
| ar_mem_cpy(rec_config_, sizeof(struct pal_st_recognition_config), |
| config, sizeof(struct pal_st_recognition_config)); |
| ar_mem_cpy((uint8_t *)rec_config_ + config->data_offset, |
| config->data_size, |
| (uint8_t *)config + config->data_offset, |
| config->data_size); |
| } |
| exit: |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| return status; |
| } |
| |
| bool StreamSoundTrigger::compareRecognitionConfig( |
| const struct pal_st_recognition_config *current_config, |
| struct pal_st_recognition_config *new_config) { |
| uint32_t i = 0, j = 0; |
| |
| if (!current_config || !new_config) |
| return false; |
| |
| /* |
| * Sometimes if the number of user confidence levels is 0, the |
| * pal_st_confidence_level struct will be different between the two |
| * configs. So all the values must be checked instead of a memcmp of the |
| * whole configs. |
| */ |
| if ((current_config->capture_handle != new_config->capture_handle) || |
| (current_config->capture_device != new_config->capture_device) || |
| (current_config->capture_requested != new_config->capture_requested) || |
| (current_config->num_phrases != new_config->num_phrases) || |
| (current_config->data_size != new_config->data_size) || |
| (current_config->data_offset != new_config->data_offset) || |
| std::memcmp((char *) current_config + current_config->data_offset, |
| (char *) new_config + new_config->data_offset, |
| current_config->data_size)) { |
| return false; |
| } else { |
| for (i = 0; i < current_config->num_phrases; i++) { |
| if ((current_config->phrases[i].id != |
| new_config->phrases[i].id) || |
| (current_config->phrases[i].recognition_modes != |
| new_config->phrases[i].recognition_modes) || |
| (current_config->phrases[i].confidence_level != |
| new_config->phrases[i].confidence_level) || |
| (current_config->phrases[i].num_levels != |
| new_config->phrases[i].num_levels)) { |
| return false; |
| } else { |
| for (j = 0; j < current_config->phrases[i].num_levels; j++) { |
| if ((current_config->phrases[i].levels[j].user_id != |
| new_config->phrases[i].levels[j].user_id) || |
| (current_config->phrases[i].levels[j].level != |
| new_config->phrases[i].levels[j].level)) |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| } |
| |
| int32_t StreamSoundTrigger::notifyClient(bool detection) { |
| int32_t status = 0; |
| struct pal_st_recognition_event *rec_event = nullptr; |
| uint32_t event_size; |
| ChronoSteadyClock_t notify_time; |
| uint64_t total_process_duration = 0; |
| bool lock_status = false; |
| |
| status = vui_intf_->GenerateCallbackEvent(this, |
| &rec_event, |
| &event_size, |
| detection); |
| if (status || !rec_event) { |
| PAL_ERR(LOG_TAG, "Failed to generate callback event"); |
| return status; |
| } |
| if (callback_) { |
| // update stream state to stopped before unlock stream mutex |
| currentState = STREAM_STOPPED; |
| notify_time = std::chrono::steady_clock::now(); |
| total_process_duration = |
| std::chrono::duration_cast<std::chrono::milliseconds>( |
| notify_time - gsl_engine_->GetDetectedTime()).count(); |
| PAL_INFO(LOG_TAG, "Notify detection event to client," |
| " total processing time: %llums", |
| (long long)total_process_duration); |
| mStreamMutex.unlock(); |
| callback_((pal_stream_handle_t *)this, 0, (uint32_t *)rec_event, |
| event_size, (uint64_t)rec_config_->cookie); |
| |
| /* |
| * client may call unload when we are doing callback with mutex |
| * unlocked, which will be blocked in second stage thread exiting |
| * as it needs notifyClient to finish. Try lock mutex and check |
| * stream states when try lock fails so that we can skip lock |
| * when stream is already stopped by client. |
| */ |
| do { |
| lock_status = mStreamMutex.try_lock(); |
| } while (!lock_status && (GetCurrentStateId() == ST_STATE_DETECTED || |
| GetCurrentStateId() == ST_STATE_BUFFERING)); |
| |
| /* |
| * NOTE: Not unlock stream mutex here if mutex is locked successfully |
| * in above loop to make stream mutex lock/unlock consistent in vairous |
| * cases for calling SetEngineDetectionState(caller of notifyClient). |
| * This is because SetEngineDetectionState may also be called by |
| * gsl engine to notify GMM detected with second stage enabled, and in |
| * this case notifyClient is not called, so we need to unlock stream |
| * mutex at end of SetEngineDetectionState, that's why we don't need |
| * to unlock stream mutex here. |
| * If mutex is not locked here, mark mutex_unlocked_after_cb_ as true |
| * so that we can avoid double unlock in SetEngineDetectionState. |
| */ |
| if (!lock_status) |
| mutex_unlocked_after_cb_ = true; |
| } |
| |
| free(rec_event); |
| |
| PAL_DBG(LOG_TAG, "Exit, status %d", status); |
| return status; |
| } |
| |
| void StreamSoundTrigger::SetDetectedToEngines(bool detected) { |
| for (auto& eng: engines_) { |
| if (eng->GetEngineId() != ST_SM_ID_SVA_F_STAGE_GMM) { |
| PAL_VERBOSE(LOG_TAG, "Notify detection event %d to engine %d", |
| detected, eng->GetEngineId()); |
| eng->GetEngine()->SetDetected(detected); |
| } |
| } |
| } |
| |
| pal_device_id_t StreamSoundTrigger::GetAvailCaptureDevice(){ |
| if (vui_ptfm_info_->GetSupportDevSwitch() && |
| rm->isDeviceAvailable(PAL_DEVICE_IN_WIRED_HEADSET)) |
| return PAL_DEVICE_IN_HEADSET_VA_MIC; |
| else |
| return PAL_DEVICE_IN_HANDSET_VA_MIC; |
| } |
| |
| void StreamSoundTrigger::AddEngine(std::shared_ptr<EngineCfg> engine_cfg) { |
| for (int32_t i = 0; i < engines_.size(); i++) { |
| if (engines_[i] == engine_cfg) { |
| PAL_VERBOSE(LOG_TAG, "engine type %d already exists", |
| engine_cfg->id_); |
| return; |
| } |
| } |
| PAL_VERBOSE(LOG_TAG, "Add engine %d, gsl_engine %p", engine_cfg->id_, |
| gsl_engine_.get()); |
| engines_.push_back(engine_cfg); |
| } |
| |
| std::shared_ptr<CaptureProfile> StreamSoundTrigger::GetCurrentCaptureProfile() { |
| std::shared_ptr<CaptureProfile> cap_prof = nullptr; |
| bool is_transit_to_nlpi = false; |
| |
| is_transit_to_nlpi = rm->CheckForForcedTransitToNonLPI(); |
| |
| if (GetAvailCaptureDevice() == PAL_DEVICE_IN_HEADSET_VA_MIC) { |
| if (is_transit_to_nlpi) { |
| cap_prof = sm_cfg_->GetCaptureProfile( |
| std::make_pair(ST_OPERATING_MODE_HIGH_PERF_AND_CHARGING, |
| ST_INPUT_MODE_HEADSET)); |
| } else if (use_lpi_) { |
| cap_prof = sm_cfg_->GetCaptureProfile( |
| std::make_pair(ST_OPERATING_MODE_LOW_POWER, |
| ST_INPUT_MODE_HEADSET)); |
| } else { |
| cap_prof = sm_cfg_->GetCaptureProfile( |
| std::make_pair(ST_OPERATING_MODE_HIGH_PERF, |
| ST_INPUT_MODE_HEADSET)); |
| } |
| } else { |
| if (is_transit_to_nlpi) { |
| cap_prof = sm_cfg_->GetCaptureProfile( |
| std::make_pair(ST_OPERATING_MODE_HIGH_PERF_AND_CHARGING, |
| ST_INPUT_MODE_HANDSET)); |
| } else if (use_lpi_) { |
| cap_prof = sm_cfg_->GetCaptureProfile( |
| std::make_pair(ST_OPERATING_MODE_LOW_POWER, |
| ST_INPUT_MODE_HANDSET)); |
| } else { |
| cap_prof = sm_cfg_->GetCaptureProfile( |
| std::make_pair(ST_OPERATING_MODE_HIGH_PERF, |
| ST_INPUT_MODE_HANDSET)); |
| } |
| } |
| |
| if (cap_prof) { |
| PAL_DBG(LOG_TAG, "cap_prof %s: dev_id=0x%x, chs=%d, sr=%d, snd_name=%s, ec_ref=%d", |
| cap_prof->GetName().c_str(), cap_prof->GetDevId(), |
| cap_prof->GetChannels(), cap_prof->GetSampleRate(), |
| cap_prof->GetSndName().c_str(), cap_prof->isECRequired()); |
| } |
| |
| return cap_prof; |
| } |
| |
| void StreamSoundTrigger::AddState(StState* state) { |
| st_states_.insert(std::make_pair(state->GetStateId(), state)); |
| } |
| |
| int32_t StreamSoundTrigger::GetCurrentStateId() { |
| if (cur_state_) |
| return cur_state_->GetStateId(); |
| |
| return ST_STATE_NONE; |
| } |
| |
| int32_t StreamSoundTrigger::GetPreviousStateId() { |
| if (prev_state_) |
| return prev_state_->GetStateId(); |
| |
| return ST_STATE_NONE; |
| } |
| |
| void StreamSoundTrigger::TransitTo(int32_t state_id) { |
| auto it = st_states_.find(state_id); |
| if (it == st_states_.end()) { |
| PAL_ERR(LOG_TAG, "Unknown transit state %d ", state_id); |
| return; |
| } |
| prev_state_ = cur_state_; |
| cur_state_ = it->second; |
| auto oldState = stStateNameMap.at(prev_state_->GetStateId()); |
| auto newState = stStateNameMap.at(it->first); |
| PAL_DBG(LOG_TAG, "Stream instance %u: state transitioned from %s to %s", |
| mInstanceID, oldState.c_str(), newState.c_str()); |
| } |
| |
| int32_t StreamSoundTrigger::ProcessInternalEvent( |
| std::shared_ptr<StEventConfig> ev_cfg) { |
| return cur_state_->ProcessEvent(ev_cfg); |
| } |
| |
| int32_t StreamSoundTrigger::StIdle::ProcessEvent( |
| std::shared_ptr<StEventConfig> ev_cfg) { |
| |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "StIdle: handle event %d for stream instance %u", |
| ev_cfg->id_, st_stream_.mInstanceID); |
| |
| switch (ev_cfg->id_) { |
| case ST_EV_LOAD_SOUND_MODEL: { |
| std::shared_ptr<CaptureProfile> cap_prof = nullptr; |
| StLoadEventConfigData *data = |
| (StLoadEventConfigData *)ev_cfg->data_.get(); |
| class SoundTriggerUUID uuid; |
| struct pal_st_sound_model * pal_st_sm; |
| |
| pal_st_sm = (struct pal_st_sound_model *)data->data_; |
| |
| uuid.timeLow = (uint32_t)pal_st_sm->vendor_uuid.timeLow; |
| uuid.timeMid = (uint16_t)pal_st_sm->vendor_uuid.timeMid; |
| uuid.timeHiAndVersion = (uint16_t)pal_st_sm->vendor_uuid.timeHiAndVersion; |
| uuid.clockSeq = (uint16_t)pal_st_sm->vendor_uuid.clockSeq; |
| uuid.node[0] = (uint8_t)pal_st_sm->vendor_uuid.node[0]; |
| uuid.node[1] = (uint8_t)pal_st_sm->vendor_uuid.node[1]; |
| uuid.node[2] = (uint8_t)pal_st_sm->vendor_uuid.node[2]; |
| uuid.node[3] = (uint8_t)pal_st_sm->vendor_uuid.node[3]; |
| uuid.node[4] = (uint8_t)pal_st_sm->vendor_uuid.node[4]; |
| uuid.node[5] = (uint8_t)pal_st_sm->vendor_uuid.node[5]; |
| |
| PAL_INFO(LOG_TAG, "Input vendor uuid : %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", |
| uuid.timeLow, |
| uuid.timeMid, |
| uuid.timeHiAndVersion, |
| uuid.clockSeq, |
| uuid.node[0], |
| uuid.node[1], |
| uuid.node[2], |
| uuid.node[3], |
| uuid.node[4], |
| uuid.node[5]); |
| |
| st_stream_.sm_cfg_ = st_stream_.vui_ptfm_info_->GetStreamConfig(uuid); |
| |
| if (!st_stream_.sm_cfg_) { |
| PAL_ERR(LOG_TAG, "Failed to get sound model platform info"); |
| status = -EINVAL; |
| goto err_exit; |
| } |
| |
| if (!st_stream_.mDevices.size()) { |
| std::shared_ptr<Device> dev = nullptr; |
| |
| // update best device |
| pal_device_id_t dev_id = st_stream_.GetAvailCaptureDevice(); |
| PAL_DBG(LOG_TAG, "Select available caputre device %d", dev_id); |
| |
| dev = st_stream_.GetPalDevice(&st_stream_, dev_id); |
| if (!dev) { |
| PAL_ERR(LOG_TAG, "Device creation is failed"); |
| status = -EINVAL; |
| goto err_exit; |
| } |
| st_stream_.mDevices.push_back(dev); |
| dev = nullptr; |
| } |
| |
| cap_prof = st_stream_.GetCurrentCaptureProfile(); |
| st_stream_.cap_prof_ = cap_prof; |
| st_stream_.mDevPPSelector = cap_prof->GetName(); |
| PAL_DBG(LOG_TAG, "devicepp selector: %s", st_stream_.mDevPPSelector.c_str()); |
| status = st_stream_.LoadSoundModel(pal_st_sm); |
| |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to load sm, status %d", status); |
| goto err_exit; |
| } else { |
| PAL_VERBOSE(LOG_TAG, "Opened the engine and dev successfully"); |
| TransitTo(ST_STATE_LOADED); |
| break; |
| } |
| err_exit: |
| break; |
| } |
| case ST_EV_UNLOAD_SOUND_MODEL: { |
| if (st_stream_.mInstanceID == 0) { |
| PAL_DBG(LOG_TAG, "No model is loaded, ignore unload"); |
| break; |
| } |
| |
| for (auto& eng: st_stream_.engines_) { |
| PAL_DBG(LOG_TAG, "Unload engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->UnloadSoundModel(&st_stream_); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Unload engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| } |
| free(eng->sm_data_); |
| } |
| |
| if (st_stream_.device_opened_ && st_stream_.mDevices.size() > 0) { |
| status = st_stream_.mDevices[0]->close(); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to close device, status %d", |
| status); |
| } |
| } |
| |
| st_stream_.mDevices.clear(); |
| |
| if(st_stream_.gsl_engine_) |
| st_stream_.gsl_engine_->ResetBufferReaders(st_stream_.reader_list_); |
| if (st_stream_.reader_) { |
| delete st_stream_.reader_; |
| st_stream_.reader_ = nullptr; |
| } |
| st_stream_.engines_.clear(); |
| if(st_stream_.gsl_engine_) |
| st_stream_.gsl_engine_->DetachStream(&st_stream_, true); |
| st_stream_.reader_list_.clear(); |
| if (st_stream_.vui_intf_) |
| st_stream_.vui_intf_->DeregisterModel(&st_stream_); |
| st_stream_.rm->resetStreamInstanceID( |
| &st_stream_, |
| st_stream_.mInstanceID); |
| break; |
| } |
| case ST_EV_PAUSE: { |
| st_stream_.paused_ = true; |
| break; |
| } |
| case ST_EV_RESUME: { |
| st_stream_.paused_ = false; |
| break; |
| } |
| case ST_EV_READ_BUFFER: { |
| status = -EIO; |
| break; |
| } |
| case ST_EV_DEVICE_DISCONNECTED: { |
| StDeviceDisconnectedEventConfigData *data = |
| (StDeviceDisconnectedEventConfigData *)ev_cfg->data_.get(); |
| pal_device_id_t device_id = data->dev_id_; |
| if (st_stream_.mDevices.size() == 0) { |
| PAL_DBG(LOG_TAG, "No device to disconnect"); |
| break; |
| } else { |
| int curr_device_id = st_stream_.mDevices[0]->getSndDeviceId(); |
| pal_device_id_t curr_device = |
| static_cast<pal_device_id_t>(curr_device_id); |
| if (curr_device != device_id) { |
| PAL_ERR(LOG_TAG, "Device %d not connected, ignore", |
| device_id); |
| break; |
| } |
| } |
| st_stream_.mDevices.clear(); |
| break; |
| } |
| case ST_EV_DEVICE_CONNECTED: { |
| std::shared_ptr<Device> dev = nullptr; |
| StDeviceConnectedEventConfigData *data = |
| (StDeviceConnectedEventConfigData *)ev_cfg->data_.get(); |
| pal_device_id_t dev_id = data->dev_id_; |
| |
| // mDevices should be empty as we have just disconnected device |
| if (st_stream_.mDevices.size() != 0) { |
| PAL_ERR(LOG_TAG, "Invalid operation"); |
| status = -EINVAL; |
| goto connect_err; |
| } |
| |
| //sm_cfg_ must be initialized, if there was any device associated |
| // with this stream earlier |
| if (!st_stream_.sm_cfg_) { |
| PAL_DBG(LOG_TAG, "Skip device connection as it will be handled in sound model load"); |
| goto connect_err; |
| } |
| |
| dev = st_stream_.GetPalDevice(&st_stream_, dev_id); |
| if (!dev) { |
| PAL_ERR(LOG_TAG, "Device creation failed"); |
| status = -EINVAL; |
| goto connect_err; |
| } |
| |
| st_stream_.mDevices.push_back(dev); |
| connect_err: |
| break; |
| } |
| case ST_EV_CONCURRENT_STREAM: { |
| // Avoid handling concurrency before sound model loaded |
| if (!st_stream_.sm_config_) |
| break; |
| std::shared_ptr<CaptureProfile> new_cap_prof = nullptr; |
| bool active = false; |
| |
| if (ev_cfg->id_ == ST_EV_CONCURRENT_STREAM) { |
| StConcurrentStreamEventConfigData *data = |
| (StConcurrentStreamEventConfigData *)ev_cfg->data_.get(); |
| active = data->is_active_; |
| } |
| new_cap_prof = st_stream_.GetCurrentCaptureProfile(); |
| if (new_cap_prof && (st_stream_.cap_prof_ != new_cap_prof)) { |
| PAL_DBG(LOG_TAG, |
| "current capture profile %s: dev_id=0x%x, chs=%d, sr=%d, ec_ref=%d\n", |
| st_stream_.cap_prof_->GetName().c_str(), |
| st_stream_.cap_prof_->GetDevId(), |
| st_stream_.cap_prof_->GetChannels(), |
| st_stream_.cap_prof_->GetSampleRate(), |
| st_stream_.cap_prof_->isECRequired()); |
| PAL_DBG(LOG_TAG, |
| "new capture profile %s: dev_id=0x%x, chs=%d, sr=%d, ec_ref=%d\n", |
| new_cap_prof->GetName().c_str(), |
| new_cap_prof->GetDevId(), |
| new_cap_prof->GetChannels(), |
| new_cap_prof->GetSampleRate(), |
| new_cap_prof->isECRequired()); |
| if (active) { |
| if (!st_stream_.mDevices.size()) { |
| std::shared_ptr<Device> dev = nullptr; |
| |
| // update best device |
| pal_device_id_t dev_id = st_stream_.GetAvailCaptureDevice(); |
| PAL_DBG(LOG_TAG, "Select available caputre device %d", dev_id); |
| |
| dev = st_stream_.GetPalDevice(&st_stream_, dev_id); |
| if (!dev) { |
| PAL_ERR(LOG_TAG, "Device creation is failed"); |
| status = -EINVAL; |
| goto err_concurrent; |
| } |
| st_stream_.mDevices.push_back(dev); |
| dev = nullptr; |
| } |
| |
| st_stream_.cap_prof_ = new_cap_prof; |
| st_stream_.mDevPPSelector = new_cap_prof->GetName(); |
| PAL_DBG(LOG_TAG, "devicepp selector: %s", |
| st_stream_.mDevPPSelector.c_str()); |
| |
| status = st_stream_.gsl_engine_->LoadSoundModel(&st_stream_, |
| st_stream_.gsl_engine_model_, |
| st_stream_.gsl_engine_model_size_); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to load sound model, status %d", |
| status); |
| goto err_concurrent; |
| } |
| |
| if (st_stream_.rec_config_) { |
| status = st_stream_.gsl_engine_->UpdateConfLevels(&st_stream_, |
| st_stream_.rec_config_, st_stream_.gsl_conf_levels_, |
| st_stream_.gsl_conf_levels_size_); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to update conf levels, status %d", |
| status); |
| goto err_unload; |
| } |
| } |
| |
| TransitTo(ST_STATE_LOADED); |
| if (st_stream_.isStarted()) { |
| std::shared_ptr<StEventConfig> ev_cfg1( |
| new StStartRecognitionEventConfig(false)); |
| status = st_stream_.ProcessInternalEvent(ev_cfg1); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to Start, status %d", status); |
| } |
| } |
| } |
| } else { |
| PAL_INFO(LOG_TAG,"no action needed, same capture profile"); |
| } |
| break; |
| err_unload: |
| status = st_stream_.gsl_engine_->UnloadSoundModel(&st_stream_); |
| if (0 != status) |
| PAL_ERR(LOG_TAG, "Failed to unload sound model, status %d", status); |
| |
| err_concurrent: |
| break; |
| } |
| case ST_EV_SSR_OFFLINE: |
| if (st_stream_.state_for_restore_ == ST_STATE_NONE) { |
| st_stream_.state_for_restore_ = ST_STATE_IDLE; |
| } |
| TransitTo(ST_STATE_SSR); |
| break; |
| default: { |
| PAL_DBG(LOG_TAG, "Unhandled event %d", ev_cfg->id_); |
| break; |
| } |
| } |
| |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::StLoaded::ProcessEvent( |
| std::shared_ptr<StEventConfig> ev_cfg) { |
| |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "StLoaded: handle event %d for stream instance %u", |
| ev_cfg->id_, st_stream_.mInstanceID); |
| |
| switch (ev_cfg->id_) { |
| case ST_EV_UNLOAD_SOUND_MODEL: { |
| int ret = 0; |
| |
| for (auto& eng: st_stream_.engines_) { |
| PAL_DBG(LOG_TAG, "Unload engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->UnloadSoundModel(&st_stream_); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Unload engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| status = ret; |
| } |
| free(eng->sm_data_); |
| } |
| |
| if (st_stream_.device_opened_ && st_stream_.mDevices.size() > 0) { |
| status = st_stream_.mDevices[0]->close(); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to close device, status %d", |
| status); |
| } |
| } |
| |
| st_stream_.mDevices.clear(); |
| |
| st_stream_.gsl_engine_->ResetBufferReaders(st_stream_.reader_list_); |
| if (st_stream_.reader_) { |
| delete st_stream_.reader_; |
| st_stream_.reader_ = nullptr; |
| } |
| st_stream_.engines_.clear(); |
| st_stream_.gsl_engine_->DetachStream(&st_stream_, true); |
| st_stream_.reader_list_.clear(); |
| if (st_stream_.vui_intf_) |
| st_stream_.vui_intf_->DeregisterModel(&st_stream_); |
| st_stream_.rm->resetStreamInstanceID( |
| &st_stream_, |
| st_stream_.mInstanceID); |
| st_stream_.notification_state_ = ENGINE_IDLE; |
| TransitTo(ST_STATE_IDLE); |
| break; |
| } |
| case ST_EV_RECOGNITION_CONFIG: { |
| StRecognitionCfgEventConfigData *data = |
| (StRecognitionCfgEventConfigData *)ev_cfg->data_.get(); |
| status = st_stream_.SendRecognitionConfig( |
| (struct pal_st_recognition_config *)data->data_); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to send recognition config, status %d", |
| status); |
| } |
| break; |
| } |
| case ST_EV_RESUME: { |
| st_stream_.paused_ = false; |
| if (!st_stream_.isStarted()) { |
| // Possible if App has stopped recognition during active |
| // concurrency. |
| break; |
| } |
| // Update conf levels in case conf level is set to 100 in pause |
| if (st_stream_.rec_config_) { |
| status = st_stream_.SendRecognitionConfig(st_stream_.rec_config_); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to send recognition config, status %d", |
| status); |
| break; |
| } |
| } |
| // fall through to start |
| [[fallthrough]]; |
| } |
| case ST_EV_START_RECOGNITION: { |
| if (st_stream_.paused_) { |
| break; // Concurrency is active, start later. |
| } |
| StStartRecognitionEventConfigData *data = |
| (StStartRecognitionEventConfigData *)ev_cfg->data_.get(); |
| if (!st_stream_.rec_config_) { |
| PAL_ERR(LOG_TAG, "Recognition config not set %d", data->restart_); |
| status = -EINVAL; |
| break; |
| } |
| |
| /* Update cap dev based on mode and configuration and start it */ |
| struct pal_device dattr; |
| bool backend_update = false; |
| std::vector<std::shared_ptr<SoundTriggerEngine>> tmp_engines; |
| std::shared_ptr<CaptureProfile> cap_prof = nullptr; |
| |
| /* |
| * Update common capture profile only in: |
| * 1. start recognition excuted |
| * 2. resume excuted and current common capture profile is null |
| */ |
| if (!st_stream_.common_cp_update_disable_ && |
| (ev_cfg->id_ == ST_EV_START_RECOGNITION || |
| (ev_cfg->id_ == ST_EV_RESUME && |
| !st_stream_.rm->GetSoundTriggerCaptureProfile()))) { |
| backend_update = st_stream_.rm->UpdateSoundTriggerCaptureProfile( |
| &st_stream_, true); |
| if (backend_update) { |
| status = rm->StopOtherDetectionStreams(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to stop other SVA streams"); |
| } |
| |
| status = rm->StartOtherDetectionStreams(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to start other SVA streams"); |
| } |
| } |
| } |
| |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| dev->getDeviceAttributes(&dattr); |
| |
| cap_prof = st_stream_.rm->GetSoundTriggerCaptureProfile(); |
| if (!cap_prof) { |
| PAL_ERR(LOG_TAG, "Invalid capture profile"); |
| goto err_exit; |
| } |
| |
| dattr.config.bit_width = cap_prof->GetBitWidth(); |
| dattr.config.ch_info.channels = cap_prof->GetChannels(); |
| dattr.config.sample_rate = cap_prof->GetSampleRate(); |
| dev->setDeviceAttributes(dattr); |
| |
| dev->setSndName(cap_prof->GetSndName()); |
| if (!st_stream_.device_opened_) { |
| status = dev->open(); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Device open failed, status %d", status); |
| break; |
| } |
| st_stream_.device_opened_ = true; |
| } |
| /* now start the device */ |
| PAL_DBG(LOG_TAG, "Start device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| |
| status = dev->start(); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Device start failed, status %d", status); |
| dev->close(); |
| st_stream_.device_opened_ = false; |
| break; |
| } else if (!rm->isDeviceActive_l(dev, &st_stream_)) { |
| st_stream_.rm->registerDevice(dev, &st_stream_); |
| } |
| PAL_DBG(LOG_TAG, "device started"); |
| } |
| |
| /* Start the engines */ |
| for (auto& eng: st_stream_.engines_) { |
| PAL_VERBOSE(LOG_TAG, "Start st engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->StartRecognition(&st_stream_); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Start st engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| goto err_exit; |
| } else { |
| tmp_engines.push_back(eng->GetEngine()); |
| } |
| } |
| |
| if (st_stream_.reader_) |
| st_stream_.reader_->reset(); |
| |
| TransitTo(ST_STATE_ACTIVE); |
| break; |
| |
| err_exit: |
| for (auto& eng: tmp_engines) |
| eng->StopRecognition(&st_stream_); |
| |
| if (st_stream_.mDevices.size() > 0) { |
| if (rm->isDeviceActive_l(st_stream_.mDevices[0], &st_stream_)) |
| st_stream_.rm->deregisterDevice(st_stream_.mDevices[0], &st_stream_); |
| st_stream_.mDevices[0]->stop(); |
| st_stream_.mDevices[0]->close(); |
| st_stream_.device_opened_ = false; |
| } |
| |
| break; |
| } |
| case ST_EV_PAUSE: { |
| st_stream_.paused_ = true; |
| break; |
| } |
| case ST_EV_READ_BUFFER: { |
| status = -EIO; |
| break; |
| } |
| case ST_EV_DEVICE_DISCONNECTED:{ |
| StDeviceDisconnectedEventConfigData *data = |
| (StDeviceDisconnectedEventConfigData *)ev_cfg->data_.get(); |
| pal_device_id_t device_id = data->dev_id_; |
| if (st_stream_.mDevices.size() == 0) { |
| PAL_DBG(LOG_TAG, "No device to disconnect"); |
| break; |
| } else { |
| int curr_device_id = st_stream_.mDevices[0]->getSndDeviceId(); |
| pal_device_id_t curr_device = |
| static_cast<pal_device_id_t>(curr_device_id); |
| if (curr_device != device_id) { |
| PAL_ERR(LOG_TAG, "Device %d not connected, ignore", |
| device_id); |
| break; |
| } |
| } |
| for (auto& device: st_stream_.mDevices) { |
| st_stream_.gsl_engine_->DisconnectSessionDevice(&st_stream_, |
| st_stream_.mStreamAttr->type, device); |
| if (st_stream_.device_opened_) { |
| status = device->close(); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "device %d close failed with status %d", |
| device->getSndDeviceId(), status); |
| } |
| st_stream_.device_opened_ = false; |
| } |
| } |
| st_stream_.mDevices.clear(); |
| break; |
| } |
| case ST_EV_DEVICE_CONNECTED: { |
| std::shared_ptr<Device> dev = nullptr; |
| StDeviceConnectedEventConfigData *data = |
| (StDeviceConnectedEventConfigData *)ev_cfg->data_.get(); |
| pal_device_id_t dev_id = data->dev_id_; |
| std::vector<std::shared_ptr<SoundTriggerEngine>> tmp_engines; |
| |
| // mDevices should be empty as we have just disconnected device |
| if (st_stream_.mDevices.size() != 0) { |
| PAL_ERR(LOG_TAG, "Invalid operation"); |
| status = -EINVAL; |
| goto connect_err; |
| } |
| |
| dev = st_stream_.GetPalDevice(&st_stream_, dev_id); |
| if (!dev) { |
| PAL_ERR(LOG_TAG, "Dev creation failed"); |
| status = -EINVAL; |
| goto connect_err; |
| } |
| st_stream_.mDevices.push_back(dev); |
| |
| PAL_DBG(LOG_TAG, "Update capture profile and stream attr in device switch"); |
| st_stream_.cap_prof_ = st_stream_.GetCurrentCaptureProfile(); |
| st_stream_.mDevPPSelector = st_stream_.cap_prof_->GetName(); |
| PAL_DBG(LOG_TAG, "Devicepp Selector: %s", |
| st_stream_.mDevPPSelector.c_str()); |
| st_stream_.updateStreamAttributes(); |
| |
| status = st_stream_.gsl_engine_->SetupSessionDevice(&st_stream_, |
| st_stream_.mStreamAttr->type, dev); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, |
| "setupSessionDevice for %d failed with status %d", |
| dev->getSndDeviceId(), status); |
| st_stream_.mDevices.pop_back(); |
| goto connect_err; |
| } |
| |
| if (!st_stream_.device_opened_) { |
| status = dev->open(); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "device %d open failed with status %d", |
| dev->getSndDeviceId(), status); |
| goto connect_err; |
| } |
| st_stream_.device_opened_ = true; |
| } |
| |
| if (st_stream_.isStarted() && !st_stream_.paused_) { |
| status = dev->start(); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "device %d start failed with status %d", |
| dev->getSndDeviceId(), status); |
| dev->close(); |
| st_stream_.device_opened_ = false; |
| goto connect_err; |
| } |
| } |
| |
| status = st_stream_.gsl_engine_->ConnectSessionDevice(&st_stream_, |
| st_stream_.mStreamAttr->type, dev); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, |
| "connectSessionDevice for %d failed with status %d", |
| dev->getSndDeviceId(), status); |
| st_stream_.mDevices.pop_back(); |
| dev->close(); |
| st_stream_.device_opened_ = false; |
| } else if (st_stream_.isStarted() && !st_stream_.paused_) { |
| if (!rm->isDeviceActive_l(dev, &st_stream_)) |
| st_stream_.rm->registerDevice(dev, &st_stream_); |
| if (st_stream_.second_stage_processing_) { |
| /* Start the engines */ |
| for (auto& eng: st_stream_.engines_) { |
| PAL_VERBOSE(LOG_TAG, "Start st engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->StartRecognition(&st_stream_); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Start st engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| goto err_start; |
| } else { |
| tmp_engines.push_back(eng->GetEngine()); |
| } |
| } |
| |
| if (st_stream_.reader_) |
| st_stream_.reader_->reset(); |
| st_stream_.second_stage_processing_ = false; |
| } else { |
| st_stream_.gsl_engine_->UpdateStateToActive(); |
| } |
| TransitTo(ST_STATE_ACTIVE); |
| } |
| break; |
| err_start: |
| for (auto& eng: tmp_engines) |
| eng->StopRecognition(&st_stream_); |
| |
| if (st_stream_.mDevices.size() > 0) { |
| st_stream_.rm->deregisterDevice(st_stream_.mDevices[0], &st_stream_); |
| st_stream_.mDevices[0]->stop(); |
| st_stream_.mDevices[0]->close(); |
| st_stream_.device_opened_ = false; |
| } |
| connect_err: |
| break; |
| } |
| case ST_EV_CONCURRENT_STREAM: { |
| std::shared_ptr<CaptureProfile> new_cap_prof = nullptr; |
| bool active = false; |
| |
| if (ev_cfg->id_ == ST_EV_CONCURRENT_STREAM) { |
| StConcurrentStreamEventConfigData *data = |
| (StConcurrentStreamEventConfigData *)ev_cfg->data_.get(); |
| active = data->is_active_; |
| } |
| new_cap_prof = st_stream_.GetCurrentCaptureProfile(); |
| if (new_cap_prof && (st_stream_.cap_prof_ != new_cap_prof)) { |
| PAL_DBG(LOG_TAG, |
| "current capture profile %s: dev_id=0x%x, chs=%d, sr=%d, ec_ref=%d\n", |
| st_stream_.cap_prof_->GetName().c_str(), |
| st_stream_.cap_prof_->GetDevId(), |
| st_stream_.cap_prof_->GetChannels(), |
| st_stream_.cap_prof_->GetSampleRate(), |
| st_stream_.cap_prof_->isECRequired()); |
| PAL_DBG(LOG_TAG, |
| "new capture profile %s: dev_id=0x%x, chs=%d, sr=%d, ec_ref=%d\n", |
| new_cap_prof->GetName().c_str(), |
| new_cap_prof->GetDevId(), |
| new_cap_prof->GetChannels(), |
| new_cap_prof->GetSampleRate(), |
| new_cap_prof->isECRequired()); |
| if (!active) { |
| if (st_stream_.device_opened_ && st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| status = dev->close(); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "device %d close failed with status %d", |
| dev->getSndDeviceId(), status); |
| } |
| st_stream_.device_opened_ = false; |
| } |
| st_stream_.mDevices.clear(); |
| |
| status = st_stream_.gsl_engine_->ReconfigureDetectionGraph(&st_stream_); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to reconfigure gsl engine, status %d", |
| status); |
| goto err_concurrent; |
| } |
| TransitTo(ST_STATE_IDLE); |
| } else { |
| PAL_ERR(LOG_TAG, "Invalid operation"); |
| status = -EINVAL; |
| } |
| } else { |
| PAL_INFO(LOG_TAG,"no action needed, same capture profile"); |
| } |
| err_concurrent: |
| break; |
| } |
| case ST_EV_SSR_OFFLINE: { |
| if (st_stream_.state_for_restore_ == ST_STATE_NONE) { |
| st_stream_.state_for_restore_ = ST_STATE_LOADED; |
| } |
| std::shared_ptr<StEventConfig> ev_cfg(new StUnloadEventConfig()); |
| status = st_stream_.ProcessInternalEvent(ev_cfg); |
| TransitTo(ST_STATE_SSR); |
| break; |
| } |
| case ST_EV_EC_REF: { |
| StECRefEventConfigData *data = |
| (StECRefEventConfigData *)ev_cfg->data_.get(); |
| Stream *s = static_cast<Stream *>(&st_stream_); |
| status = st_stream_.gsl_engine_->setECRef(s, data->dev_, |
| data->is_enable_, st_stream_.ec_rx_dev_ == nullptr ); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to set EC Ref in gsl engine"); |
| } |
| break; |
| } |
| case ST_EV_DETECTED: { |
| PAL_DBG(LOG_TAG, |
| "Keyword detected with invalid state, stop engines"); |
| /* |
| * When detection is ignored here, stop engines to make sure |
| * engines are in proper state for next detection/start. For |
| * multi VA cases, gsl engine stop is same as restart. |
| */ |
| for (auto& eng: st_stream_.engines_) { |
| PAL_VERBOSE(LOG_TAG, "Stop engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->StopRecognition(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Stop engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| } |
| } |
| break; |
| } |
| default: { |
| PAL_DBG(LOG_TAG, "Unhandled event %d", ev_cfg->id_); |
| break; |
| } |
| } |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::StActive::ProcessEvent( |
| std::shared_ptr<StEventConfig> ev_cfg) { |
| |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "StActive: handle event %d for stream instance %u", |
| ev_cfg->id_, st_stream_.mInstanceID); |
| |
| switch (ev_cfg->id_) { |
| case ST_EV_DETECTED: { |
| StDetectedEventConfigData *data = |
| (StDetectedEventConfigData *) ev_cfg->data_.get(); |
| if (data->det_type_ != GMM_DETECTED) |
| break; |
| if (!st_stream_.rec_config_->capture_requested && |
| st_stream_.engines_.size() == 1) { |
| TransitTo(ST_STATE_DETECTED); |
| if (st_stream_.GetCurrentStateId() == ST_STATE_DETECTED) { |
| st_stream_.PostDelayedStop(); |
| } |
| } else { |
| if (st_stream_.engines_.size() > 1) |
| st_stream_.second_stage_processing_ = true; |
| TransitTo(ST_STATE_BUFFERING); |
| st_stream_.SetDetectedToEngines(true); |
| } |
| if (st_stream_.engines_.size() == 1) { |
| st_stream_.notifyClient(true); |
| } |
| break; |
| } |
| case ST_EV_PAUSE: { |
| st_stream_.paused_ = true; |
| // fall through to stop |
| [[fallthrough]]; |
| } |
| case ST_EV_UNLOAD_SOUND_MODEL: |
| case ST_EV_STOP_RECOGNITION: { |
| // Do not update capture profile when pausing stream |
| bool backend_update = false; |
| if (!st_stream_.common_cp_update_disable_ && |
| (ev_cfg->id_ == ST_EV_STOP_RECOGNITION || |
| ev_cfg->id_ == ST_EV_UNLOAD_SOUND_MODEL)) { |
| backend_update = st_stream_.rm->UpdateSoundTriggerCaptureProfile( |
| &st_stream_, false); |
| if (backend_update) { |
| status = rm->StopOtherDetectionStreams(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to stop other SVA streams"); |
| } |
| } |
| } |
| |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_VERBOSE(LOG_TAG, "Deregister device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| if (rm->isDeviceActive_l(dev, &st_stream_)) |
| st_stream_.rm->deregisterDevice(dev, &st_stream_); |
| } |
| for (auto& eng: st_stream_.engines_) { |
| PAL_VERBOSE(LOG_TAG, "Stop engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->StopRecognition(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Stop engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| } |
| } |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_DBG(LOG_TAG, "Stop device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| status = dev->stop(); |
| if (status) |
| PAL_ERR(LOG_TAG, "Device stop failed, status %d", status); |
| |
| PAL_DBG(LOG_TAG, "Close device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| |
| status = dev->close(); |
| st_stream_.device_opened_ = false; |
| if (status) |
| PAL_ERR(LOG_TAG, "Device close failed, status %d", status); |
| } |
| |
| if (backend_update) { |
| status = rm->StartOtherDetectionStreams(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to start other SVA streams"); |
| } |
| } |
| TransitTo(ST_STATE_LOADED); |
| if (ev_cfg->id_ == ST_EV_UNLOAD_SOUND_MODEL) { |
| status = st_stream_.ProcessInternalEvent(ev_cfg); |
| if (status != 0) { |
| PAL_ERR(LOG_TAG, "Failed to unload sound model, status = %d", |
| status); |
| } |
| } |
| break; |
| } |
| case ST_EV_EC_REF: { |
| StECRefEventConfigData *data = |
| (StECRefEventConfigData *)ev_cfg->data_.get(); |
| Stream *s = static_cast<Stream *>(&st_stream_); |
| status = st_stream_.gsl_engine_->setECRef(s, data->dev_, |
| data->is_enable_, st_stream_.ec_rx_dev_ == nullptr); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to set EC Ref in gsl engine"); |
| } |
| break; |
| } |
| case ST_EV_READ_BUFFER: { |
| status = -EIO; |
| break; |
| } |
| case ST_EV_DEVICE_DISCONNECTED: { |
| StDeviceDisconnectedEventConfigData *data = |
| (StDeviceDisconnectedEventConfigData *)ev_cfg->data_.get(); |
| pal_device_id_t device_id = data->dev_id_; |
| if (st_stream_.mDevices.size() == 0) { |
| PAL_DBG(LOG_TAG, "No device to disconnect"); |
| break; |
| } else { |
| int curr_device_id = st_stream_.mDevices[0]->getSndDeviceId(); |
| pal_device_id_t curr_device = |
| static_cast<pal_device_id_t>(curr_device_id); |
| if (curr_device != device_id) { |
| PAL_ERR(LOG_TAG, "Device %d not connected, ignore", |
| device_id); |
| break; |
| } |
| } |
| for (auto& device: st_stream_.mDevices) { |
| if (rm->isDeviceActive_l(device, &st_stream_)) |
| st_stream_.rm->deregisterDevice(device, &st_stream_); |
| st_stream_.gsl_engine_->DisconnectSessionDevice(&st_stream_, |
| st_stream_.mStreamAttr->type, device); |
| |
| status = device->stop(); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "device stop failed with status %d", status); |
| goto disconnect_err; |
| } |
| |
| status = device->close(); |
| st_stream_.device_opened_ = false; |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "device close failed with status %d", status); |
| goto disconnect_err; |
| } |
| } |
| disconnect_err: |
| st_stream_.mDevices.clear(); |
| break; |
| } |
| case ST_EV_DEVICE_CONNECTED: { |
| std::shared_ptr<Device> dev = nullptr; |
| StDeviceConnectedEventConfigData *data = |
| (StDeviceConnectedEventConfigData *)ev_cfg->data_.get(); |
| pal_device_id_t dev_id = data->dev_id_; |
| |
| // mDevices should be empty as we have just disconnected device |
| if (st_stream_.mDevices.size() != 0) { |
| PAL_ERR(LOG_TAG, "Invalid operation"); |
| status = -EINVAL; |
| goto connect_err; |
| } |
| |
| dev = st_stream_.GetPalDevice(&st_stream_, dev_id); |
| if (!dev) { |
| PAL_ERR(LOG_TAG, "Device creation failed"); |
| status = -EINVAL; |
| goto connect_err; |
| } |
| st_stream_.mDevices.push_back(dev); |
| |
| PAL_DBG(LOG_TAG, "Update capture profile and stream attr in device switch"); |
| st_stream_.cap_prof_ = st_stream_.GetCurrentCaptureProfile(); |
| st_stream_.mDevPPSelector = st_stream_.cap_prof_->GetName(); |
| PAL_DBG(LOG_TAG, "devicepp selector: %s", |
| st_stream_.mDevPPSelector.c_str()); |
| st_stream_.updateStreamAttributes(); |
| |
| status = st_stream_.gsl_engine_->SetupSessionDevice(&st_stream_, |
| st_stream_.mStreamAttr->type, dev); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "setupSessionDevice for %d failed with status %d", |
| dev->getSndDeviceId(), status); |
| st_stream_.mDevices.pop_back(); |
| goto connect_err; |
| } |
| |
| if (!st_stream_.device_opened_) { |
| status = dev->open(); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "device %d open failed with status %d", |
| dev->getSndDeviceId(), status); |
| goto connect_err; |
| } |
| st_stream_.device_opened_ = true; |
| } |
| |
| status = dev->start(); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "device %d start failed with status %d", |
| dev->getSndDeviceId(), status); |
| dev->close(); |
| st_stream_.device_opened_ = false; |
| goto connect_err; |
| } |
| |
| status = st_stream_.gsl_engine_->ConnectSessionDevice(&st_stream_, |
| st_stream_.mStreamAttr->type, dev); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "connectSessionDevice for %d failed with status %d", |
| dev->getSndDeviceId(), status); |
| st_stream_.mDevices.pop_back(); |
| dev->close(); |
| st_stream_.device_opened_ = false; |
| } else if (!rm->isDeviceActive_l(dev, &st_stream_)) { |
| st_stream_.rm->registerDevice(dev, &st_stream_); |
| } |
| connect_err: |
| break; |
| } |
| case ST_EV_CONCURRENT_STREAM: { |
| std::shared_ptr<CaptureProfile> new_cap_prof = nullptr; |
| bool active = false; |
| |
| if (ev_cfg->id_ == ST_EV_CONCURRENT_STREAM) { |
| StConcurrentStreamEventConfigData *data = |
| (StConcurrentStreamEventConfigData *)ev_cfg->data_.get(); |
| active = data->is_active_; |
| } |
| new_cap_prof = st_stream_.GetCurrentCaptureProfile(); |
| if (new_cap_prof && (st_stream_.cap_prof_ != new_cap_prof)) { |
| PAL_DBG(LOG_TAG, |
| "current capture profile %s: dev_id=0x%x, chs=%d, sr=%d, ec_ref=%d\n", |
| st_stream_.cap_prof_->GetName().c_str(), |
| st_stream_.cap_prof_->GetDevId(), |
| st_stream_.cap_prof_->GetChannels(), |
| st_stream_.cap_prof_->GetSampleRate(), |
| st_stream_.cap_prof_->isECRequired()); |
| PAL_DBG(LOG_TAG, |
| "new capture profile %s: dev_id=0x%x, chs=%d, sr=%d, ec_ref=%d\n", |
| new_cap_prof->GetName().c_str(), |
| new_cap_prof->GetDevId(), |
| new_cap_prof->GetChannels(), |
| new_cap_prof->GetSampleRate(), |
| new_cap_prof->isECRequired()); |
| if (!active) { |
| std::shared_ptr<StEventConfig> ev_cfg1( |
| new StStopRecognitionEventConfig(false)); |
| status = st_stream_.ProcessInternalEvent(ev_cfg1); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to Stop, status %d", status); |
| break; |
| } |
| |
| status = st_stream_.ProcessInternalEvent(ev_cfg); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to Unload, status %d", status); |
| break; |
| } |
| } else { |
| PAL_ERR(LOG_TAG, "Invalid operation"); |
| status = -EINVAL; |
| } |
| } else { |
| PAL_INFO(LOG_TAG,"no action needed, same capture profile"); |
| } |
| break; |
| } |
| case ST_EV_SSR_OFFLINE: { |
| if (st_stream_.state_for_restore_ == ST_STATE_NONE) { |
| st_stream_.state_for_restore_ = ST_STATE_ACTIVE; |
| } |
| std::shared_ptr<StEventConfig> ev_cfg1( |
| new StStopRecognitionEventConfig(false)); |
| status = st_stream_.ProcessInternalEvent(ev_cfg1); |
| |
| std::shared_ptr<StEventConfig> ev_cfg2( |
| new StUnloadEventConfig()); |
| status = st_stream_.ProcessInternalEvent(ev_cfg2); |
| TransitTo(ST_STATE_SSR); |
| break; |
| } |
| default: { |
| PAL_DBG(LOG_TAG, "Unhandled event %d", ev_cfg->id_); |
| break; |
| } |
| } |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::StDetected::ProcessEvent( |
| std::shared_ptr<StEventConfig> ev_cfg) { |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "StDetected: handle event %d for stream instance %u", |
| ev_cfg->id_, st_stream_.mInstanceID); |
| |
| switch (ev_cfg->id_) { |
| case ST_EV_START_RECOGNITION: { |
| // Client restarts next recognition without config changed. |
| st_stream_.CancelDelayedStop(); |
| |
| for (auto& eng: st_stream_.engines_) { |
| PAL_VERBOSE(LOG_TAG, "Restart engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->RestartRecognition(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Restart engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| } |
| } |
| if (st_stream_.reader_) |
| st_stream_.reader_->reset(); |
| if (!status) { |
| TransitTo(ST_STATE_ACTIVE); |
| } else { |
| TransitTo(ST_STATE_LOADED); |
| } |
| rm->releaseWakeLock(); |
| break; |
| } |
| case ST_EV_PAUSE: { |
| st_stream_.CancelDelayedStop(); |
| st_stream_.paused_ = true; |
| // fall through to stop |
| [[fallthrough]]; |
| } |
| case ST_EV_UNLOAD_SOUND_MODEL: |
| case ST_EV_STOP_RECOGNITION: { |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_VERBOSE(LOG_TAG, "Deregister device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| if (rm->isDeviceActive_l(dev, &st_stream_)) |
| st_stream_.rm->deregisterDevice(dev, &st_stream_); |
| } |
| |
| st_stream_.CancelDelayedStop(); |
| for (auto& eng: st_stream_.engines_) { |
| PAL_VERBOSE(LOG_TAG, "Stop engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->StopRecognition(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Stop engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| } |
| } |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_DBG(LOG_TAG, "Stop device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| status = dev->stop(); |
| if (status) |
| PAL_ERR(LOG_TAG, "Device stop failed, status %d", status); |
| |
| status = dev->close(); |
| st_stream_.device_opened_ = false; |
| if (status) |
| PAL_ERR(LOG_TAG, "Device close failed, status %d", status); |
| } |
| TransitTo(ST_STATE_LOADED); |
| |
| if (ev_cfg->id_ == ST_EV_UNLOAD_SOUND_MODEL) { |
| status = st_stream_.ProcessInternalEvent(ev_cfg); |
| if (status != 0) { |
| PAL_ERR(LOG_TAG, "Failed to unload sound model, status = %d", |
| status); |
| } |
| } |
| rm->releaseWakeLock(); |
| break; |
| } |
| case ST_EV_RECOGNITION_CONFIG: { |
| StRecognitionCfgEventConfigData *data = |
| (StRecognitionCfgEventConfigData *)ev_cfg->data_.get(); |
| if (st_stream_.compareRecognitionConfig(st_stream_.rec_config_, |
| (struct pal_st_recognition_config *)data->data_)) { |
| PAL_DBG(LOG_TAG, "Same recognition config, skip update"); |
| break; |
| } |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_VERBOSE(LOG_TAG, "Deregister device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| if (rm->isDeviceActive_l(dev, &st_stream_)) |
| st_stream_.rm->deregisterDevice(dev, &st_stream_); |
| } |
| /* |
| * Client can update config for next recognition. |
| * Get to loaded state as START event will start recognition. |
| */ |
| st_stream_.CancelDelayedStop(); |
| |
| for (auto& eng: st_stream_.engines_) { |
| PAL_VERBOSE(LOG_TAG, "Stop engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->StopRecognition(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Stop engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| } |
| } |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_DBG(LOG_TAG, "Stop device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| status = dev->stop(); |
| if (status) |
| PAL_ERR(LOG_TAG, "Device stop failed, status %d", status); |
| |
| status = dev->close(); |
| st_stream_.device_opened_ = false; |
| if (status) |
| PAL_ERR(LOG_TAG, "Device close failed, status %d", status); |
| } |
| TransitTo(ST_STATE_LOADED); |
| status = st_stream_.ProcessInternalEvent(ev_cfg); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to handle recognition config, status %d", |
| status); |
| } |
| rm->releaseWakeLock(); |
| // START event will be handled in loaded state. |
| break; |
| } |
| case ST_EV_RESUME: { |
| st_stream_.paused_ = false; |
| break; |
| } |
| case ST_EV_CONCURRENT_STREAM: |
| case ST_EV_DEVICE_DISCONNECTED: |
| case ST_EV_DEVICE_CONNECTED: { |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_VERBOSE(LOG_TAG, "Deregister device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| if (rm->isDeviceActive_l(dev, &st_stream_)) |
| st_stream_.rm->deregisterDevice(dev, &st_stream_); |
| } |
| |
| st_stream_.CancelDelayedStop(); |
| for (auto& eng: st_stream_.engines_) { |
| PAL_VERBOSE(LOG_TAG, "Stop engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->StopRecognition(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Stop engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| } |
| } |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_DBG(LOG_TAG, "Stop device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| status = dev->stop(); |
| if (status) |
| PAL_ERR(LOG_TAG, "Device stop failed, status %d", status); |
| |
| status = dev->close(); |
| st_stream_.device_opened_ = false; |
| if (0 != status) |
| PAL_ERR(LOG_TAG, "device close failed with status %d", status); |
| } |
| TransitTo(ST_STATE_LOADED); |
| status = st_stream_.ProcessInternalEvent(ev_cfg); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to handle device connection, status %d", |
| status); |
| } |
| rm->releaseWakeLock(); |
| break; |
| } |
| case ST_EV_SSR_OFFLINE: { |
| if (st_stream_.state_for_restore_ == ST_STATE_NONE) { |
| st_stream_.state_for_restore_ = ST_STATE_LOADED; |
| } |
| std::shared_ptr<StEventConfig> ev_cfg1( |
| new StStopRecognitionEventConfig(false)); |
| status = st_stream_.ProcessInternalEvent(ev_cfg1); |
| |
| std::shared_ptr<StEventConfig> ev_cfg2( |
| new StUnloadEventConfig()); |
| status = st_stream_.ProcessInternalEvent(ev_cfg2); |
| TransitTo(ST_STATE_SSR); |
| break; |
| } |
| case ST_EV_EC_REF: { |
| StECRefEventConfigData *data = |
| (StECRefEventConfigData *)ev_cfg->data_.get(); |
| Stream *s = static_cast<Stream *>(&st_stream_); |
| status = st_stream_.gsl_engine_->setECRef(s, data->dev_, |
| data->is_enable_, st_stream_.ec_rx_dev_ == nullptr); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to set EC Ref in gsl engine"); |
| } |
| break; |
| } |
| default: { |
| PAL_DBG(LOG_TAG, "Unhandled event %d", ev_cfg->id_); |
| break; |
| } |
| } |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::StBuffering::ProcessEvent( |
| std::shared_ptr<StEventConfig> ev_cfg) { |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "StBuffering: handle event %d for stream instance %u", |
| ev_cfg->id_, st_stream_.mInstanceID); |
| |
| switch (ev_cfg->id_) { |
| case ST_EV_READ_BUFFER: { |
| StReadBufferEventConfigData *data = |
| (StReadBufferEventConfigData *)ev_cfg->data_.get(); |
| struct pal_buffer *buf = (struct pal_buffer *)data->data_; |
| |
| if (!st_stream_.reader_) { |
| PAL_ERR(LOG_TAG, "no reader exists"); |
| status = -EINVAL; |
| break; |
| } |
| status = st_stream_.reader_->read(buf->buffer, buf->size); |
| if (st_stream_.vui_ptfm_info_->GetEnableDebugDumps()) { |
| ST_DBG_FILE_WRITE(st_stream_.lab_fd_, buf->buffer, buf->size); |
| } |
| break; |
| } |
| case ST_EV_STOP_BUFFERING: { |
| /* |
| * Buffering continues in GSL engine side until RestartRecognition |
| * called, to avoid ADSP stuck if client takes some time to send |
| * start after stop buffering. |
| */ |
| if (st_stream_.force_nlpi_vote) { |
| rm->voteSleepMonitor(&st_stream_, false, true); |
| st_stream_.force_nlpi_vote = false; |
| } |
| if (st_stream_.reader_) |
| st_stream_.reader_->updateState(READER_DISABLED); |
| |
| // post delayed stop in case client does not send next start |
| st_stream_.PostDelayedStop(); |
| break; |
| } |
| case ST_EV_START_RECOGNITION: { |
| /* |
| * Can happen if client requests next recognition without any config |
| * change with/without reading buffers after sending detection event. |
| */ |
| if (st_stream_.force_nlpi_vote) { |
| rm->voteSleepMonitor(&st_stream_, false, true); |
| st_stream_.force_nlpi_vote = false; |
| } |
| StStartRecognitionEventConfigData *data = |
| (StStartRecognitionEventConfigData *)ev_cfg->data_.get(); |
| PAL_DBG(LOG_TAG, "StBuffering: start recognition, is restart %d", |
| data->restart_); |
| st_stream_.CancelDelayedStop(); |
| |
| for (auto& eng: st_stream_.engines_) { |
| PAL_VERBOSE(LOG_TAG, "Restart engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->RestartRecognition(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Restart engine %d buffering failed, status %d", |
| eng->GetEngineId(), status); |
| break; |
| } |
| } |
| if (st_stream_.reader_) |
| st_stream_.reader_->reset(); |
| if (!status) { |
| TransitTo(ST_STATE_ACTIVE); |
| } else { |
| TransitTo(ST_STATE_LOADED); |
| } |
| rm->releaseWakeLock(); |
| break; |
| } |
| case ST_EV_RECOGNITION_CONFIG: { |
| StRecognitionCfgEventConfigData *data = |
| (StRecognitionCfgEventConfigData *)ev_cfg->data_.get(); |
| if (st_stream_.compareRecognitionConfig(st_stream_.rec_config_, |
| (struct pal_st_recognition_config *)data->data_)) { |
| PAL_DBG(LOG_TAG, "Same recognition config, skip update"); |
| break; |
| } |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_VERBOSE(LOG_TAG, "Deregister device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| if (rm->isDeviceActive_l(dev, &st_stream_)) |
| st_stream_.rm->deregisterDevice(dev, &st_stream_); |
| } |
| |
| /* |
| * Can happen if client doesn't read buffers after sending detection |
| * event, but requests next recognition with config change. |
| * Get to loaded state as START event will start the recognition. |
| */ |
| if (st_stream_.force_nlpi_vote) { |
| rm->voteSleepMonitor(&st_stream_, false, true); |
| st_stream_.force_nlpi_vote = false; |
| } |
| st_stream_.CancelDelayedStop(); |
| |
| for (auto& eng: st_stream_.engines_) { |
| PAL_VERBOSE(LOG_TAG, "Stop engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->StopRecognition(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Stop engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| } |
| } |
| if (st_stream_.reader_) { |
| st_stream_.reader_->reset(); |
| } |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_DBG(LOG_TAG, "Stop device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| status = dev->stop(); |
| if (status) |
| PAL_ERR(LOG_TAG, "Device stop failed, status %d", status); |
| |
| status = dev->close(); |
| st_stream_.device_opened_ = false; |
| if (0 != status) |
| PAL_ERR(LOG_TAG, "device close failed with status %d", status); |
| } |
| TransitTo(ST_STATE_LOADED); |
| status = st_stream_.ProcessInternalEvent(ev_cfg); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to handle recognition config, status %d", |
| status); |
| } |
| rm->releaseWakeLock(); |
| // START event will be handled in loaded state. |
| break; |
| } |
| case ST_EV_PAUSE: { |
| st_stream_.paused_ = true; |
| PAL_DBG(LOG_TAG, "StBuffering: Pause"); |
| // fall through to stop |
| [[fallthrough]]; |
| } |
| case ST_EV_UNLOAD_SOUND_MODEL: |
| case ST_EV_STOP_RECOGNITION: { |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_VERBOSE(LOG_TAG, "Deregister device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| if (rm->isDeviceActive_l(dev, &st_stream_)) |
| st_stream_.rm->deregisterDevice(dev, &st_stream_); |
| } |
| |
| // Possible with deffered stop if client doesn't start next recognition. |
| if (st_stream_.force_nlpi_vote) { |
| rm->voteSleepMonitor(&st_stream_, false, true); |
| st_stream_.force_nlpi_vote = false; |
| } |
| st_stream_.CancelDelayedStop(); |
| |
| for (auto& eng: st_stream_.engines_) { |
| PAL_VERBOSE(LOG_TAG, "Stop engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->StopRecognition(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Stop engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| } |
| } |
| if (st_stream_.reader_) { |
| st_stream_.reader_->reset(); |
| } |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_DBG(LOG_TAG, "Stop device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| status = dev->stop(); |
| if (status) |
| PAL_ERR(LOG_TAG, "Device stop failed, status %d", status); |
| |
| status = dev->close(); |
| st_stream_.device_opened_ = false; |
| if (status) |
| PAL_ERR(LOG_TAG, "Device close failed, status %d", status); |
| } |
| TransitTo(ST_STATE_LOADED); |
| if (ev_cfg->id_ == ST_EV_UNLOAD_SOUND_MODEL) { |
| status = st_stream_.ProcessInternalEvent(ev_cfg); |
| if (status != 0) { |
| PAL_ERR(LOG_TAG, "Failed to unload sound model, status = %d", |
| status); |
| } |
| } |
| rm->releaseWakeLock(); |
| break; |
| } |
| case ST_EV_DETECTED: { |
| // Second stage detections fall here. |
| StDetectedEventConfigData *data = |
| (StDetectedEventConfigData *)ev_cfg->data_.get(); |
| if (data->det_type_ == GMM_DETECTED) { |
| break; |
| } |
| // If second stage has rejected, stop buffering and restart recognition |
| if (data->det_type_ == KEYWORD_DETECTION_REJECT || |
| data->det_type_ == USER_VERIFICATION_REJECT) { |
| if (st_stream_.rejection_notified_) { |
| PAL_DBG(LOG_TAG, "Already notified client with second stage rejection"); |
| break; |
| } |
| |
| PAL_DBG(LOG_TAG, "Second stage rejected, type %d", |
| data->det_type_); |
| |
| for (auto& eng : st_stream_.engines_) { |
| if ((data->det_type_ == USER_VERIFICATION_REJECT && |
| eng->GetEngineId() & ST_SM_ID_SVA_S_STAGE_KWD) || |
| (data->det_type_ == KEYWORD_DETECTION_REJECT && |
| eng->GetEngineId() & ST_SM_ID_SVA_S_STAGE_USER)) { |
| |
| status = eng->GetEngine()->StopRecognition(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to stop recognition for engines"); |
| } |
| } |
| } |
| st_stream_.second_stage_processing_ = false; |
| st_stream_.detection_state_ = ENGINE_IDLE; |
| |
| if (st_stream_.reader_) { |
| st_stream_.reader_->reset(); |
| } |
| |
| if (st_stream_.vui_ptfm_info_->GetNotifySecondStageFailure()) { |
| st_stream_.rejection_notified_ = true; |
| st_stream_.notifyClient(false); |
| if (!st_stream_.rec_config_->capture_requested && |
| st_stream_.GetCurrentStateId() == ST_STATE_BUFFERING) |
| st_stream_.PostDelayedStop(); |
| } else { |
| PAL_DBG(LOG_TAG, "Notification for second stage rejection is disabled"); |
| for (auto& eng : st_stream_.engines_) { |
| status = eng->GetEngine()->RestartRecognition(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Restart engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| break; |
| } |
| } |
| if (!status) { |
| TransitTo(ST_STATE_ACTIVE); |
| } else { |
| TransitTo(ST_STATE_LOADED); |
| } |
| } |
| rm->releaseWakeLock(); |
| break; |
| } |
| if (data->det_type_ == KEYWORD_DETECTION_SUCCESS || |
| data->det_type_ == USER_VERIFICATION_SUCCESS) { |
| st_stream_.detection_state_ |= data->det_type_; |
| } |
| // notify client until both keyword detection/user verification done |
| if (st_stream_.detection_state_ == st_stream_.notification_state_) { |
| PAL_DBG(LOG_TAG, "Second stage detected"); |
| st_stream_.second_stage_processing_ = false; |
| st_stream_.detection_state_ = ENGINE_IDLE; |
| if (!st_stream_.rec_config_->capture_requested) { |
| if (st_stream_.reader_) { |
| st_stream_.reader_->reset(); |
| } |
| TransitTo(ST_STATE_DETECTED); |
| } |
| st_stream_.notifyClient(true); |
| if (!st_stream_.rec_config_->capture_requested && |
| (st_stream_.GetCurrentStateId() == ST_STATE_BUFFERING || |
| st_stream_.GetCurrentStateId() == ST_STATE_DETECTED)) { |
| st_stream_.PostDelayedStop(); |
| } |
| } |
| break; |
| } |
| case ST_EV_CONCURRENT_STREAM: |
| case ST_EV_DEVICE_DISCONNECTED: |
| case ST_EV_DEVICE_CONNECTED: { |
| if (st_stream_.force_nlpi_vote) { |
| rm->voteSleepMonitor(&st_stream_, false, true); |
| st_stream_.force_nlpi_vote = false; |
| } |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_VERBOSE(LOG_TAG, "Deregister device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| if (rm->isDeviceActive_l(dev, &st_stream_)) |
| st_stream_.rm->deregisterDevice(dev, &st_stream_); |
| } |
| |
| st_stream_.CancelDelayedStop(); |
| |
| for (auto& eng: st_stream_.engines_) { |
| PAL_VERBOSE(LOG_TAG, "Stop engine %d", eng->GetEngineId()); |
| status = eng->GetEngine()->StopRecognition(&st_stream_); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Stop engine %d failed, status %d", |
| eng->GetEngineId(), status); |
| } |
| } |
| if (st_stream_.reader_) { |
| st_stream_.reader_->reset(); |
| } |
| if (st_stream_.mDevices.size() > 0) { |
| auto& dev = st_stream_.mDevices[0]; |
| PAL_DBG(LOG_TAG, "Stop device %d-%s", dev->getSndDeviceId(), |
| dev->getPALDeviceName().c_str()); |
| status = dev->stop(); |
| if (status) |
| PAL_ERR(LOG_TAG, "Device stop failed, status %d", status); |
| |
| status = dev->close(); |
| st_stream_.device_opened_ = false; |
| if (status) |
| PAL_ERR(LOG_TAG, "Device close failed, status %d", status); |
| } |
| TransitTo(ST_STATE_LOADED); |
| status = st_stream_.ProcessInternalEvent(ev_cfg); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to handle device connection, status %d", |
| status); |
| } |
| rm->releaseWakeLock(); |
| // device connection event will be handled in loaded state. |
| break; |
| } |
| case ST_EV_SSR_OFFLINE: { |
| if (st_stream_.state_for_restore_ == ST_STATE_NONE) { |
| if (st_stream_.second_stage_processing_) |
| st_stream_.state_for_restore_ = ST_STATE_ACTIVE; |
| else |
| st_stream_.state_for_restore_ = ST_STATE_LOADED; |
| } |
| |
| std::shared_ptr<StEventConfig> ev_cfg2( |
| new StStopRecognitionEventConfig(false)); |
| status = st_stream_.ProcessInternalEvent(ev_cfg2); |
| |
| std::shared_ptr<StEventConfig> ev_cfg3( |
| new StUnloadEventConfig()); |
| status = st_stream_.ProcessInternalEvent(ev_cfg3); |
| TransitTo(ST_STATE_SSR); |
| break; |
| } |
| case ST_EV_EC_REF: { |
| StECRefEventConfigData *data = |
| (StECRefEventConfigData *)ev_cfg->data_.get(); |
| Stream *s = static_cast<Stream *>(&st_stream_); |
| status = st_stream_.gsl_engine_->setECRef(s, data->dev_, |
| data->is_enable_, st_stream_.ec_rx_dev_ == nullptr); |
| if (status) { |
| PAL_ERR(LOG_TAG, "Failed to set EC Ref in gsl engine"); |
| } |
| break; |
| } |
| default: { |
| PAL_DBG(LOG_TAG, "Unhandled event %d", ev_cfg->id_); |
| break; |
| } |
| } |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::StSSR::ProcessEvent( |
| std::shared_ptr<StEventConfig> ev_cfg) { |
| int32_t status = 0; |
| |
| PAL_DBG(LOG_TAG, "StSSR: handle event %d for stream instance %u", |
| ev_cfg->id_, st_stream_.mInstanceID); |
| |
| switch (ev_cfg->id_) { |
| case ST_EV_SSR_ONLINE: { |
| TransitTo(ST_STATE_IDLE); |
| /* |
| * sm_config_ can be NULL if load sound model is failed in |
| * previous SSR online event. This scenario can occur if |
| * back to back SSR happens in less than 1 sec. |
| */ |
| if (!st_stream_.sm_config_) { |
| PAL_ERR(LOG_TAG, "sound model config is NULL"); |
| break; |
| } |
| PAL_INFO(LOG_TAG, "stream state for restore %d", st_stream_.state_for_restore_); |
| /* |
| * For concurrent stream handling, ST state transition |
| * occurs from loaded/active to idle and then back to loaded/active. |
| * While transitioning back to loaded/active, in case of any failures |
| * because of ongoing SSR or other failures restore state remains in idle |
| * and sm cfg will be non NULL in this scenario. |
| * In this case, reset the state_for_restore based on actual stream state |
| * for recovery to happen properly after SSR. |
| */ |
| if (st_stream_.state_for_restore_ == ST_STATE_IDLE) { |
| if (st_stream_.currentState == STREAM_STARTED) |
| st_stream_.state_for_restore_ = ST_STATE_ACTIVE; |
| else if (st_stream_.currentState == STREAM_OPENED || |
| st_stream_.currentState == STREAM_STOPPED) |
| st_stream_.state_for_restore_ = ST_STATE_LOADED; |
| } |
| if (st_stream_.state_for_restore_ == ST_STATE_LOADED || |
| st_stream_.state_for_restore_ == ST_STATE_ACTIVE) { |
| std::shared_ptr<StEventConfig> ev_cfg1( |
| new StLoadEventConfig(st_stream_.sm_config_)); |
| status = st_stream_.ProcessInternalEvent(ev_cfg1); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to load sound model, status %d", |
| status); |
| break; |
| } |
| if (st_stream_.rec_config_) { |
| status = st_stream_.SendRecognitionConfig( |
| st_stream_.rec_config_); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, |
| "Failed to send recognition config, status %d", status); |
| break; |
| } |
| } |
| } |
| |
| if (st_stream_.state_for_restore_ == ST_STATE_ACTIVE) { |
| std::shared_ptr<StEventConfig> ev_cfg2( |
| new StStartRecognitionEventConfig(false)); |
| status = st_stream_.ProcessInternalEvent(ev_cfg2); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to Start, status %d", status); |
| break; |
| } |
| } |
| PAL_DBG(LOG_TAG, "StSSR: event %d handled", ev_cfg->id_); |
| st_stream_.state_for_restore_ = ST_STATE_NONE; |
| break; |
| } |
| case ST_EV_LOAD_SOUND_MODEL: { |
| if (st_stream_.state_for_restore_ != ST_STATE_IDLE) { |
| PAL_ERR(LOG_TAG, "Invalid operation, client state = %d now", |
| st_stream_.state_for_restore_); |
| status = -EINVAL; |
| } else { |
| StLoadEventConfigData *data = |
| (StLoadEventConfigData *)ev_cfg->data_.get(); |
| status = st_stream_.UpdateSoundModel( |
| (struct pal_st_sound_model *)data->data_); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to update sound model, status %d", |
| status); |
| } else { |
| st_stream_.state_for_restore_ = ST_STATE_LOADED; |
| } |
| } |
| break; |
| } |
| case ST_EV_UNLOAD_SOUND_MODEL: { |
| if (st_stream_.state_for_restore_ != ST_STATE_LOADED) { |
| PAL_ERR(LOG_TAG, "Invalid operation, client state = %d now", |
| st_stream_.state_for_restore_); |
| status = -EINVAL; |
| } else { |
| st_stream_.state_for_restore_ = ST_STATE_IDLE; |
| } |
| if (st_stream_.vui_intf_) |
| st_stream_.vui_intf_->DeregisterModel(&st_stream_); |
| break; |
| } |
| case ST_EV_RECOGNITION_CONFIG: { |
| if (st_stream_.state_for_restore_ != ST_STATE_LOADED) { |
| PAL_ERR(LOG_TAG, "Invalid operation, client state = %d now", |
| st_stream_.state_for_restore_); |
| status = -EINVAL; |
| } else { |
| StRecognitionCfgEventConfigData *data = |
| (StRecognitionCfgEventConfigData *)ev_cfg->data_.get(); |
| status = st_stream_.UpdateRecognitionConfig( |
| (struct pal_st_recognition_config *)data->data_); |
| if (0 != status) { |
| PAL_ERR(LOG_TAG, "Failed to update recognition config," |
| "status %d", status); |
| } |
| } |
| break; |
| } |
| case ST_EV_START_RECOGNITION: { |
| if (st_stream_.state_for_restore_ != ST_STATE_LOADED) { |
| PAL_ERR(LOG_TAG, "Invalid operation, client state = %d now", |
| st_stream_.state_for_restore_); |
| status = -EINVAL; |
| } else { |
| StStartRecognitionEventConfigData *data = |
| (StStartRecognitionEventConfigData *)ev_cfg->data_.get(); |
| if (!st_stream_.rec_config_) { |
| PAL_ERR(LOG_TAG, "Recognition config not set %d", data->restart_); |
| status = -EINVAL; |
| break; |
| } |
| st_stream_.state_for_restore_ = ST_STATE_ACTIVE; |
| } |
| break; |
| } |
| case ST_EV_STOP_RECOGNITION: { |
| if (st_stream_.state_for_restore_ != ST_STATE_ACTIVE) { |
| PAL_ERR(LOG_TAG, "Invalid operation, client state = %d now", |
| st_stream_.state_for_restore_); |
| status = -EINVAL; |
| } else { |
| st_stream_.state_for_restore_ = ST_STATE_LOADED; |
| } |
| break; |
| } |
| case ST_EV_PAUSE: { |
| st_stream_.paused_ = true; |
| break; |
| } |
| case ST_EV_RESUME: { |
| if (st_stream_.paused_) { |
| if (st_stream_.currentState == STREAM_STARTED) |
| st_stream_.state_for_restore_ = ST_STATE_ACTIVE; |
| st_stream_.paused_ = false; |
| } |
| break; |
| } |
| case ST_EV_READ_BUFFER: |
| status = -EIO; |
| break; |
| default: { |
| PAL_DBG(LOG_TAG, "Unhandled event %d", ev_cfg->id_); |
| break; |
| } |
| } |
| |
| return status; |
| } |
| bool StreamSoundTrigger::ConfigSupportLPI() { |
| |
| bool lpi = true; |
| bool config_support_lpi = true; |
| |
| if (sm_cfg_ && sm_cfg_->GetVUIFirstStageConfig(model_type_)) |
| config_support_lpi = |
| sm_cfg_->GetVUIFirstStageConfig(model_type_)->IsLpiSupported(); |
| |
| if (!config_support_lpi || !vui_ptfm_info_->GetLpiEnable()) |
| lpi = false; |
| |
| return lpi; |
| } |
| |
| int32_t StreamSoundTrigger::ssrDownHandler() { |
| int32_t status = 0; |
| |
| std::lock_guard<std::mutex> lck(mStreamMutex); |
| common_cp_update_disable_ = true; |
| std::shared_ptr<StEventConfig> ev_cfg(new StSSROfflineConfig()); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| common_cp_update_disable_ = false; |
| |
| return status; |
| } |
| |
| int32_t StreamSoundTrigger::ssrUpHandler() { |
| int32_t status = 0; |
| |
| std::lock_guard<std::mutex> lck(mStreamMutex); |
| common_cp_update_disable_ = true; |
| std::shared_ptr<StEventConfig> ev_cfg(new StSSROnlineConfig()); |
| status = cur_state_->ProcessEvent(ev_cfg); |
| common_cp_update_disable_ = false; |
| |
| return status; |
| } |
| |
| bool StreamSoundTrigger::isStarted() { |
| return (currentState == STREAM_STARTED || |
| GetCurrentStateId() == ST_STATE_BUFFERING || |
| GetCurrentStateId() == ST_STATE_DETECTED); |
| } |
| |
| struct st_uuid StreamSoundTrigger::GetVendorUuid() |
| { |
| struct st_uuid uuid; |
| if (sm_config_) { |
| return sm_config_->vendor_uuid; |
| } |
| memset(&uuid, 0, sizeof(uuid)); |
| return uuid; |
| } |