AUDIO.LA.8.0.r1-09700-KAILUA.0 * tag 'AUDIO.LA.8.0.r1-09700-KAILUA.0' of https://git.codelinaro.org/clo/la/platform/vendor/qcom-opensource/audio-hal/st-hal-ar: sthal: check audio extn init result in stdev init ST-HAL: Update check for stream state in destructor Change-Id: Id6dc5c1718e5c32f020f660e2ec03b061ab3ac30
728 lines
26 KiB
C++
728 lines
26 KiB
C++
/*
|
|
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
* * Neither the name of The Linux Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
/* Changes from Qualcomm Innovation Center are provided under the following license:
|
|
*
|
|
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted (subject to the limitations in the
|
|
* disclaimer below) provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
* * Neither the name of Qualcomm Innovation Center, Inc. nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
|
|
* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
|
|
* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
|
|
*/
|
|
|
|
#define LOG_TAG "sthal_SoundTriggerSession"
|
|
#define ATRACE_TAG (ATRACE_TAG_AUDIO | ATRACE_TAG_HAL)
|
|
#define LOG_NDEBUG 0
|
|
/*#define VERY_VERY_VERBOSE_LOGGING*/
|
|
#ifdef VERY_VERY_VERBOSE_LOGGING
|
|
#define ALOGVV ALOGV
|
|
#else
|
|
#define ALOGVV(a...) do { } while(0)
|
|
#endif
|
|
|
|
#include "SoundTriggerSession.h"
|
|
|
|
#include <log/log.h>
|
|
#include <utils/Trace.h>
|
|
#include <cstring>
|
|
#include <chrono>
|
|
#include <thread>
|
|
|
|
#include "PalApi.h"
|
|
|
|
SoundTriggerSession::SoundTriggerSession(sound_model_handle_t handle,
|
|
audio_hw_call_back_t callback)
|
|
{
|
|
UpdateState(IDLE);
|
|
sm_handle_ = handle;
|
|
rec_config_payload_ = nullptr;
|
|
rec_config_ = nullptr;
|
|
hal_callback_ = callback;
|
|
pal_handle_ = nullptr;
|
|
}
|
|
|
|
SoundTriggerSession::~SoundTriggerSession()
|
|
{
|
|
if (pal_handle_)
|
|
pal_stream_close(pal_handle_);
|
|
|
|
pal_handle_ = nullptr;
|
|
|
|
if (rec_config_payload_) {
|
|
free(rec_config_payload_);
|
|
rec_config_payload_ = nullptr;
|
|
}
|
|
rec_config_ = nullptr;
|
|
}
|
|
|
|
int SoundTriggerSession::pal_callback(
|
|
pal_stream_handle_t *stream_handle,
|
|
uint32_t event_id,
|
|
uint32_t *event_data,
|
|
uint32_t event_size,
|
|
uint64_t cookie)
|
|
{
|
|
int32_t status = 0;
|
|
bool lock_status = false;
|
|
unsigned int size = 0;
|
|
int i = 0;
|
|
int j = 0;
|
|
recognition_callback_t callback;
|
|
SoundTriggerSession *session = nullptr;
|
|
struct pal_st_recognition_event *event = nullptr;
|
|
struct sound_trigger_recognition_event *st_event = nullptr;
|
|
struct sound_trigger_phrase_recognition_event *phrase_event = nullptr;
|
|
struct pal_st_phrase_recognition_event *pal_phrase_event = nullptr;
|
|
|
|
if (!stream_handle || !event_data) {
|
|
status = -EINVAL;
|
|
ALOGE("%s: error, invalid stream handle or event data", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
ALOGD("%s: stream_handle (%p), event_id (%x), event_size (%d),"
|
|
"cookie (%" PRIu64 ")", __func__, stream_handle, event_id,
|
|
event_size, cookie);
|
|
|
|
session = (SoundTriggerSession *)cookie;
|
|
/*
|
|
* Sometimes Client may call unload directly, which may get blocked
|
|
* in PAL when releasing second stage engine thread, as it is waiting
|
|
* for this callback to finish. Check if session state changes to non
|
|
* ACTIVE state.
|
|
*/
|
|
do {
|
|
lock_status = session->ses_mutex_.try_lock();
|
|
} while(!lock_status && session->IsState(ACTIVE));
|
|
|
|
if (!session->IsState(ACTIVE)) {
|
|
ALOGW("%s: skip notification as client has stopped", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
event = (struct pal_st_recognition_event *)event_data;
|
|
|
|
if (event->type == PAL_SOUND_MODEL_TYPE_GENERIC) {
|
|
// parse event and check if keyword detected
|
|
size = sizeof(struct sound_trigger_recognition_event) +
|
|
event->data_size;
|
|
st_event = (struct sound_trigger_recognition_event *)calloc(1, size);
|
|
if (!st_event) {
|
|
status = -ENOMEM;
|
|
ALOGE("%s: error, failed to allocate recognition event", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
st_event->data_offset = sizeof(struct sound_trigger_recognition_event);
|
|
} else if (event->type == PAL_SOUND_MODEL_TYPE_KEYPHRASE) {
|
|
size = sizeof(struct sound_trigger_phrase_recognition_event) +
|
|
event->data_size;
|
|
phrase_event =
|
|
(struct sound_trigger_phrase_recognition_event *)calloc(1, size);
|
|
if (!phrase_event) {
|
|
status = -ENOMEM;
|
|
ALOGE("%s: error, failed to allocate recognition event", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
st_event = (struct sound_trigger_recognition_event *)phrase_event;
|
|
st_event->data_offset =
|
|
sizeof(struct sound_trigger_phrase_recognition_event);
|
|
|
|
// copy data only related to phrase event
|
|
pal_phrase_event = (struct pal_st_phrase_recognition_event *)event;
|
|
phrase_event->num_phrases = pal_phrase_event->num_phrases;
|
|
for (i = 0; i < phrase_event->num_phrases; i++) {
|
|
phrase_event->phrase_extras[i].id =
|
|
pal_phrase_event->phrase_extras[i].id;
|
|
phrase_event->phrase_extras[i].recognition_modes =
|
|
pal_phrase_event->phrase_extras[i].recognition_modes;
|
|
phrase_event->phrase_extras[i].confidence_level =
|
|
pal_phrase_event->phrase_extras[i].confidence_level;
|
|
phrase_event->phrase_extras[i].num_levels =
|
|
pal_phrase_event->phrase_extras[i].num_levels;
|
|
|
|
for (j = 0; j < phrase_event->phrase_extras[i].num_levels; j++) {
|
|
phrase_event->phrase_extras[i].levels[j].user_id =
|
|
pal_phrase_event->phrase_extras[i].levels[j].user_id;
|
|
phrase_event->phrase_extras[i].levels[j].level =
|
|
pal_phrase_event->phrase_extras[i].levels[j].level;
|
|
}
|
|
}
|
|
} else {
|
|
ALOGE("%s: Invalid event type :%d", __func__, event->type);
|
|
status = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
// copy members inside structrue
|
|
st_event->status = event->status;
|
|
st_event->type = (sound_trigger_sound_model_type_t)event->type;
|
|
st_event->model = session->GetSoundModelHandle();
|
|
st_event->capture_available = event->capture_available;
|
|
st_event->capture_session = session->GetCaptureHandle();
|
|
st_event->capture_delay_ms = event->capture_delay_ms;
|
|
st_event->capture_preamble_ms = event->capture_preamble_ms;
|
|
st_event->trigger_in_data = event->trigger_in_data;
|
|
|
|
st_event->audio_config.sample_rate = event->media_config.sample_rate;
|
|
if (event->media_config.ch_info.channels == 1)
|
|
st_event->audio_config.channel_mask = AUDIO_CHANNEL_IN_MONO;
|
|
else if (event->media_config.ch_info.channels == 2)
|
|
st_event->audio_config.channel_mask = AUDIO_CHANNEL_IN_STEREO;
|
|
st_event->audio_config.format = AUDIO_FORMAT_PCM_16_BIT;
|
|
|
|
st_event->data_size = event->data_size;
|
|
|
|
// copy opaque data
|
|
memcpy((uint8_t *)st_event + st_event->data_offset,
|
|
(uint8_t *)event + event->data_offset,
|
|
st_event->data_size);
|
|
|
|
// callback to SoundTriggerService
|
|
session->GetRecognitionCallback(&callback);
|
|
ATRACE_BEGIN("sthal: client detection callback");
|
|
|
|
callback(st_event, session->GetCookie());
|
|
|
|
ATRACE_END();
|
|
|
|
exit:
|
|
// release resources allocated
|
|
if (phrase_event)
|
|
free(phrase_event);
|
|
else if (st_event)
|
|
free(st_event);
|
|
if (lock_status)
|
|
session->ses_mutex_.unlock();
|
|
ALOGV("%s: Exit, status %d", __func__, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
bool SoundTriggerSession::IsACDSoundModel(struct sound_trigger_sound_model *sound_model)
|
|
{
|
|
//todo: get this from PAL instead of hardcoding.
|
|
const sound_trigger_uuid_t qc_acd_uuid = { 0x4e93281b, 0x296e, 0x4d73, 0x9833,
|
|
{ 0x27, 0x10, 0xc3, 0xc7, 0xc1, 0xdb } };
|
|
|
|
if (sound_model &&
|
|
!std::memcmp(&sound_model->vendor_uuid, &qc_acd_uuid,
|
|
sizeof(sound_trigger_uuid_t)))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
int SoundTriggerSession::OpenPALStream(pal_stream_type_t stream_type)
|
|
{
|
|
int status = 0;
|
|
struct pal_stream_attributes stream_attributes;
|
|
struct pal_device device;
|
|
|
|
ALOGV("%s: Enter", __func__);
|
|
|
|
device.id = PAL_DEVICE_IN_HANDSET_VA_MIC; // To-Do: convert into PAL Device
|
|
device.config.sample_rate = 48000;
|
|
device.config.bit_width = 16;
|
|
device.config.ch_info.channels = 2;
|
|
device.config.ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
|
|
device.config.ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
|
|
device.config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE;
|
|
|
|
stream_attributes.type = stream_type;
|
|
stream_attributes.flags = (pal_stream_flags_t)0;
|
|
stream_attributes.direction = PAL_AUDIO_INPUT;
|
|
stream_attributes.in_media_config.sample_rate = 16000;
|
|
stream_attributes.in_media_config.bit_width = 16;
|
|
stream_attributes.in_media_config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE;
|
|
stream_attributes.in_media_config.ch_info.channels = 1;
|
|
stream_attributes.in_media_config.ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
|
|
|
|
ALOGD("%s:(%x:status)%d", __func__, status, __LINE__);
|
|
status = pal_stream_open(&stream_attributes,
|
|
1,
|
|
&device,
|
|
0,
|
|
nullptr,
|
|
&pal_callback,
|
|
(uint64_t)this,
|
|
&pal_handle_);
|
|
|
|
ALOGD("%s:(%x:status)%d", __func__, status, __LINE__);
|
|
|
|
if (status) {
|
|
ALOGE("%s: Pal Stream Open Error (%x)", __func__, status);
|
|
status = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
ALOGV("%s: Exit, status = %d", __func__, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
int SoundTriggerSession::StopRecognition_l()
|
|
{
|
|
int status = 0;
|
|
|
|
ALOGV("%s: Enter", __func__);
|
|
UpdateState(STOPPING);
|
|
|
|
// deregister from audio hal
|
|
RegisterHalEvent(false);
|
|
|
|
// stop pal stream
|
|
status = pal_stream_stop(pal_handle_);
|
|
if (status) {
|
|
ALOGE("%s: error, failed to stop pal stream, status = %d",
|
|
__func__, status);
|
|
}
|
|
|
|
if (rec_config_payload_) {
|
|
free(rec_config_payload_);
|
|
rec_config_payload_ = nullptr;
|
|
}
|
|
rec_config_ = nullptr;
|
|
|
|
UpdateState(STOPPED);
|
|
ALOGV("%s: Exit, status = %d", __func__, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
int SoundTriggerSession::LoadSoundModel(
|
|
struct sound_trigger_sound_model *sound_model)
|
|
{
|
|
int status = 0;
|
|
struct pal_st_sound_model *pal_common_sm = nullptr;
|
|
struct pal_st_phrase_sound_model *pal_phrase_sm = nullptr;
|
|
struct sound_trigger_sound_model *common_sm = nullptr;
|
|
struct sound_trigger_phrase_sound_model *phrase_sm = nullptr;
|
|
pal_param_payload *param_payload = nullptr;
|
|
unsigned int size = 0;
|
|
pal_stream_type_t stream_type = PAL_STREAM_VOICE_UI;
|
|
|
|
ALOGV("%s: Enter", __func__);
|
|
std::lock_guard<std::mutex> lck(ses_mutex_);
|
|
|
|
if (IsACDSoundModel(sound_model))
|
|
stream_type = PAL_STREAM_ACD;
|
|
|
|
// open pal stream
|
|
status = OpenPALStream(stream_type);
|
|
if (status) {
|
|
ALOGE("%s: error, failed to open PAL stream", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
// parse sound model into pal_sound_model
|
|
if (sound_model->type == SOUND_MODEL_TYPE_GENERIC) {
|
|
common_sm = sound_model;
|
|
if ((stream_type != PAL_STREAM_ACD) &&
|
|
(!common_sm->data_size ||
|
|
(common_sm->data_offset < sizeof(*common_sm)))) {
|
|
ALOGE("%s: Invalid Generic sound model params "
|
|
"data size=%d, data offset=%d", __func__,
|
|
common_sm->data_size, common_sm->data_offset);
|
|
status = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
size = sizeof(struct pal_st_sound_model) +
|
|
common_sm->data_size;
|
|
param_payload = (pal_param_payload *)calloc(1,
|
|
sizeof(pal_param_payload) + size);
|
|
if (!param_payload) {
|
|
ALOGE("%s: error, failed to allocate pal sound model", __func__);
|
|
status = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
param_payload->payload_size = size;
|
|
pal_common_sm = (struct pal_st_sound_model *)param_payload->payload;
|
|
|
|
pal_common_sm->type = (pal_st_sound_model_type_t)common_sm->type;
|
|
memcpy(&pal_common_sm->uuid, &common_sm->uuid,
|
|
sizeof(struct st_uuid));
|
|
memcpy(&pal_common_sm->vendor_uuid, &common_sm->vendor_uuid,
|
|
sizeof(struct st_uuid));
|
|
pal_common_sm->data_size = common_sm->data_size;
|
|
pal_common_sm->data_offset = sizeof(struct pal_st_sound_model);
|
|
|
|
// data_size is zero for ACD streams and non-zero for other streams
|
|
if (pal_common_sm->data_size)
|
|
memcpy((uint8_t *)pal_common_sm + pal_common_sm->data_offset,
|
|
(uint8_t *)common_sm + common_sm->data_offset,
|
|
pal_common_sm->data_size);
|
|
|
|
} else if (sound_model->type == SOUND_MODEL_TYPE_KEYPHRASE) {
|
|
phrase_sm = (struct sound_trigger_phrase_sound_model *)sound_model;
|
|
if ((phrase_sm->common.data_size == 0) ||
|
|
(phrase_sm->common.data_offset < sizeof(*phrase_sm)) ||
|
|
(phrase_sm->common.type != SOUND_MODEL_TYPE_KEYPHRASE) ||
|
|
(phrase_sm->num_phrases == 0)) {
|
|
ALOGE("%s: Invalid phrase sound model params "
|
|
"data size=%d, data offset=%d, type=%d phrases=%d",
|
|
__func__, phrase_sm->common.data_size,
|
|
phrase_sm->common.data_offset, phrase_sm->common.type,
|
|
phrase_sm->num_phrases);
|
|
status = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
size = sizeof(struct pal_st_phrase_sound_model) +
|
|
phrase_sm->common.data_size;
|
|
param_payload = (pal_param_payload *)calloc(1,
|
|
sizeof(pal_param_payload) + size);
|
|
if (!param_payload) {
|
|
ALOGE("%s: error, failed to allocate pal phrase sound model",
|
|
__func__);
|
|
status = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
param_payload->payload_size = size;
|
|
pal_phrase_sm =
|
|
(struct pal_st_phrase_sound_model *)param_payload->payload;
|
|
|
|
pal_phrase_sm->common.type =
|
|
(pal_st_sound_model_type_t)phrase_sm->common.type;
|
|
memcpy(&pal_phrase_sm->common.uuid, &phrase_sm->common.uuid,
|
|
sizeof(struct st_uuid));
|
|
memcpy(&pal_phrase_sm->common.vendor_uuid,
|
|
&phrase_sm->common.vendor_uuid,
|
|
sizeof(struct st_uuid));
|
|
pal_phrase_sm->common.data_size = phrase_sm->common.data_size;
|
|
pal_phrase_sm->common.data_offset =
|
|
sizeof(struct pal_st_phrase_sound_model);
|
|
pal_phrase_sm->num_phrases = phrase_sm->num_phrases;
|
|
|
|
for (int i = 0; i < pal_phrase_sm->num_phrases; i++) {
|
|
pal_phrase_sm->phrases[i].id = phrase_sm->phrases[i].id;
|
|
pal_phrase_sm->phrases[i].recognition_mode =
|
|
phrase_sm->phrases[i].recognition_mode;
|
|
pal_phrase_sm->phrases[i].num_users =
|
|
phrase_sm->phrases[i].num_users;
|
|
for (int j = 0; j < pal_phrase_sm->phrases[i].num_users; j++)
|
|
pal_phrase_sm->phrases[i].users[j] =
|
|
phrase_sm->phrases[i].users[j];
|
|
|
|
memcpy(pal_phrase_sm->phrases[i].locale,
|
|
phrase_sm->phrases[i].locale,
|
|
SOUND_TRIGGER_MAX_LOCALE_LEN);
|
|
memcpy(pal_phrase_sm->phrases[i].text,
|
|
phrase_sm->phrases[i].text,
|
|
SOUND_TRIGGER_MAX_STRING_LEN);
|
|
}
|
|
|
|
memcpy((uint8_t *)pal_phrase_sm + pal_phrase_sm->common.data_offset,
|
|
(uint8_t *)phrase_sm + phrase_sm->common.data_offset,
|
|
pal_phrase_sm->common.data_size);
|
|
pal_common_sm = (struct pal_st_sound_model *)pal_phrase_sm;
|
|
} else {
|
|
ALOGE("%s: error, unknown sound model type %d",
|
|
__func__, sound_model->type);
|
|
status = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
// load sound model with pal api
|
|
status = pal_stream_set_param(pal_handle_,
|
|
PAL_PARAM_ID_LOAD_SOUND_MODEL,
|
|
param_payload);
|
|
if (status) {
|
|
ALOGE("%s: error, failed to load sound model into PAL, status = %d",
|
|
__func__, status);
|
|
goto exit;
|
|
}
|
|
|
|
UpdateState(LOADED);
|
|
|
|
exit:
|
|
if (param_payload)
|
|
free(param_payload);
|
|
ALOGV("%s: Exit, status = %d", __func__, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
int SoundTriggerSession::UnloadSoundModel()
|
|
{
|
|
int status = 0;
|
|
|
|
ALOGV("%s: Enter", __func__);
|
|
std::lock_guard<std::mutex> lck(ses_mutex_);
|
|
if (IsState(ACTIVE)) {
|
|
status = StopRecognition_l();
|
|
if (status) {
|
|
ALOGE("%s: error, failed to stop recognition, status = %d",
|
|
__func__, status);
|
|
}
|
|
}
|
|
|
|
status = pal_stream_close(pal_handle_);
|
|
if (status) {
|
|
ALOGE("%s: error, failed to close pal stream, status = %d",
|
|
__func__, status);
|
|
}
|
|
pal_handle_ = nullptr;
|
|
if (rec_config_payload_) {
|
|
free(rec_config_payload_);
|
|
rec_config_payload_ = nullptr;
|
|
}
|
|
rec_config_ = nullptr;
|
|
|
|
UpdateState(IDLE);
|
|
|
|
ALOGV("%s: Exit, status = %d", __func__, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
int SoundTriggerSession::StartRecognition(
|
|
const struct sound_trigger_recognition_config *config,
|
|
recognition_callback_t callback,
|
|
void *cookie,
|
|
uint32_t version)
|
|
{
|
|
int status = 0;
|
|
unsigned int size = 0;
|
|
|
|
ALOGV("%s: Enter, state = %d", __func__, state_);
|
|
std::lock_guard<std::mutex> lck(ses_mutex_);
|
|
|
|
if (rec_config_payload_) {
|
|
free(rec_config_payload_); // valid due to subsequent start after a detection
|
|
rec_config_payload_ = nullptr;
|
|
}
|
|
size = sizeof(struct pal_st_recognition_config) +
|
|
config->data_size;
|
|
rec_config_payload_ = (pal_param_payload *)calloc(1,
|
|
sizeof(pal_param_payload) + size);
|
|
if (!rec_config_payload_) {
|
|
ALOGE("%s: error, failed to allocate pal recognition config", __func__);
|
|
status = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
rec_config_payload_->payload_size = size;
|
|
rec_config_ = (struct pal_st_recognition_config *)rec_config_payload_->payload;
|
|
|
|
rec_config_->capture_handle = (int32_t)config->capture_handle;
|
|
rec_config_->capture_device = (uint32_t)config->capture_device;
|
|
rec_config_->capture_requested = config->capture_requested;
|
|
rec_config_->num_phrases = config->num_phrases;
|
|
for (int i = 0; i < rec_config_->num_phrases; i++) {
|
|
rec_config_->phrases[i].id = config->phrases[i].id;
|
|
rec_config_->phrases[i].recognition_modes =
|
|
config->phrases[i].recognition_modes;
|
|
rec_config_->phrases[i].confidence_level =
|
|
config->phrases[i].confidence_level;
|
|
rec_config_->phrases[i].num_levels = config->phrases[i].num_levels;
|
|
|
|
for (int j = 0; j < rec_config_->phrases[i].num_levels; j++) {
|
|
rec_config_->phrases[i].levels[j].user_id =
|
|
config->phrases[i].levels[j].user_id;
|
|
rec_config_->phrases[i].levels[j].level =
|
|
config->phrases[i].levels[j].level;
|
|
}
|
|
}
|
|
rec_config_->data_size = config->data_size;
|
|
rec_config_->data_offset = sizeof(struct pal_st_recognition_config);
|
|
rec_config_->cookie = (uint8_t *)this;
|
|
if (version == SOUND_TRIGGER_DEVICE_API_VERSION_1_3) {
|
|
memcpy((uint8_t *)rec_config_ + rec_config_->data_offset,
|
|
(uint8_t *)config + config->data_offset -
|
|
sizeof(struct sound_trigger_recognition_config_header),
|
|
rec_config_->data_size);
|
|
} else {
|
|
memcpy((uint8_t *)rec_config_ + rec_config_->data_offset,
|
|
(uint8_t *)config + config->data_offset,
|
|
rec_config_->data_size);
|
|
}
|
|
|
|
// set recognition config
|
|
status = pal_stream_set_param(pal_handle_,
|
|
PAL_PARAM_ID_RECOGNITION_CONFIG,
|
|
rec_config_payload_);
|
|
if (status) {
|
|
ALOGE("%s: error, failed to set recognition config, status = %d",
|
|
__func__, status);
|
|
goto exit;
|
|
}
|
|
|
|
rec_callback_ = callback;
|
|
cookie_ = cookie;
|
|
|
|
// start pal stream
|
|
status = pal_stream_start(pal_handle_);
|
|
if (status) {
|
|
ALOGE("%s: error, failed to start pal stream, status = %d",
|
|
__func__, status);
|
|
goto exit;
|
|
}
|
|
UpdateState(ACTIVE);
|
|
|
|
// register to audio hal
|
|
RegisterHalEvent(true);
|
|
|
|
exit:
|
|
if (status && rec_config_payload_) {
|
|
free(rec_config_payload_);
|
|
rec_config_payload_ = nullptr;
|
|
rec_config_ = nullptr;
|
|
}
|
|
|
|
ALOGV("%s: Exit, status = %d", __func__, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
int SoundTriggerSession::StopRecognition()
|
|
{
|
|
int status = 0;
|
|
|
|
ALOGV("%s: Enter", __func__);
|
|
std::lock_guard<std::mutex> lck(ses_mutex_);
|
|
|
|
StopRecognition_l();
|
|
|
|
ALOGV("%s: Exit, status = %d", __func__, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
void SoundTriggerSession::RegisterHalEvent(bool is_register)
|
|
{
|
|
struct sound_trigger_event_info event_info;
|
|
|
|
if ((rec_config_ && rec_config_->capture_requested) && hal_callback_) {
|
|
if (is_register) {
|
|
ALOGD("%s:[c%d] ST_EVENT_SESSION_REGISTER capture_handle %d",
|
|
__func__, sm_handle_, rec_config_->capture_handle);
|
|
event_info.st_ses.p_ses = (void *)pal_handle_;
|
|
event_info.st_ses.capture_handle = rec_config_->capture_handle;
|
|
hal_callback_(ST_EVENT_SESSION_REGISTER, &event_info);
|
|
} else {
|
|
ALOGD("%s:[c%d] ST_EVENT_SESSION_DEREGISTER capture_handle %d",
|
|
__func__, sm_handle_, rec_config_->capture_handle);
|
|
event_info.st_ses.p_ses = (void *)pal_handle_;
|
|
event_info.st_ses.capture_handle = rec_config_->capture_handle;
|
|
hal_callback_(ST_EVENT_SESSION_DEREGISTER, &event_info);
|
|
}
|
|
}
|
|
}
|
|
|
|
sound_model_handle_t SoundTriggerSession::GetSoundModelHandle()
|
|
{
|
|
return sm_handle_;
|
|
}
|
|
|
|
int SoundTriggerSession::GetCaptureHandle()
|
|
{
|
|
int handle = 0;
|
|
|
|
if (rec_config_)
|
|
handle = rec_config_->capture_handle;
|
|
|
|
return handle;
|
|
}
|
|
|
|
int SoundTriggerSession::GetModuleVersion(char version[])
|
|
{
|
|
int status = 0;
|
|
pal_param_payload *param_payload = nullptr;
|
|
struct version_arch_payload *version_payload = nullptr;
|
|
|
|
ALOGV("%s: Enter", __func__);
|
|
std::lock_guard<std::mutex> lck(ses_mutex_);
|
|
status = OpenPALStream(PAL_STREAM_VOICE_UI);
|
|
if (status) {
|
|
ALOGE("%s: Failed to open pal stream, status = %d", __func__, status);
|
|
goto exit;
|
|
}
|
|
|
|
status = pal_stream_get_param(pal_handle_,
|
|
PAL_PARAM_ID_WAKEUP_MODULE_VERSION, ¶m_payload);
|
|
if (status || param_payload == NULL) {
|
|
ALOGE("%s: Failed to get version, status = %d", __func__, status);
|
|
goto exit;
|
|
}
|
|
|
|
version_payload = (struct version_arch_payload *)param_payload;
|
|
snprintf(version, SOUND_TRIGGER_MAX_STRING_LEN, "%d, %s",
|
|
version_payload->version, version_payload->arch);
|
|
|
|
exit:
|
|
if (pal_handle_) {
|
|
status = pal_stream_close(pal_handle_);
|
|
if (status) {
|
|
ALOGE("%s: error, failed to close pal stream, status = %d",
|
|
__func__, status);
|
|
}
|
|
pal_handle_ = nullptr;
|
|
}
|
|
if (param_payload != NULL)
|
|
delete param_payload;
|
|
ALOGV("%s: Exit", __func__);
|
|
return 0;
|
|
}
|
|
|
|
void *SoundTriggerSession::GetCookie()
|
|
{
|
|
return cookie_;
|
|
}
|
|
|
|
void SoundTriggerSession::GetRecognitionCallback(
|
|
recognition_callback_t *callback)
|
|
{
|
|
*callback = rec_callback_;
|
|
}
|