blob: fdd9f9d12ac82abef536ce54949faafe93ead02c [file] [log] [blame]
/*
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022, 2024, Qualcomm Innovation Center, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LOG_TAG "AHAL: AudioVoice"
#define ATRACE_TAG (ATRACE_TAG_AUDIO|ATRACE_TAG_HAL)
#define LOG_NDEBUG 0
#include <stdio.h>
#include <cutils/str_parms.h>
#include "audio_extn.h"
#include "AudioVoice.h"
#include "PalApi.h"
#include "AudioCommon.h"
#ifndef AUDIO_MODE_CALL_SCREEN
#define AUDIO_MODE_CALL_SCREEN 4
#endif
int AudioVoice::SetMode(const audio_mode_t mode) {
int ret = 0;
AHAL_DBG("Enter: mode: %d", mode);
if (mode_ != mode) {
/*start a new session for full voice call*/
if ((mode == AUDIO_MODE_CALL_SCREEN && mode_ == AUDIO_MODE_IN_CALL)||
(mode == AUDIO_MODE_IN_CALL && mode_ == AUDIO_MODE_CALL_SCREEN)){
mode_ = mode;
AHAL_DBG("call screen device switch called: %d", mode);
VoiceSetDevice(voice_.session);
} else {
mode_ = mode;
if (voice_.in_call && mode == AUDIO_MODE_NORMAL)
ret = StopCall();
else if (mode == AUDIO_MODE_CALL_SCREEN)
UpdateCalls(voice_.session);
}
}
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int AudioVoice::VoiceSetParameters(const char *kvpairs) {
int value, i;
char c_value[32];
int ret = 0, err;
struct str_parms *parms;
pal_param_payload *params = nullptr;
uint32_t tty_mode;
bool volume_boost;
bool slow_talk;
bool hd_voice;
bool hac;
parms = str_parms_create_str(kvpairs);
if (!parms)
return -EINVAL;
AHAL_DBG("Enter params: %s", kvpairs);
err = str_parms_get_int(parms, AUDIO_PARAMETER_KEY_VSID, &value);
if (err >= 0) {
uint32_t vsid = value;
int call_state = -1;
err = str_parms_get_int(parms, AUDIO_PARAMETER_KEY_CALL_STATE, &value);
if (err >= 0) {
call_state = value;
} else {
AHAL_ERR("error call_state key not found");
ret = -EINVAL;
goto done;
}
if (is_valid_vsid(vsid) && is_valid_call_state(call_state)) {
ret = UpdateCallState(vsid, call_state);
} else {
AHAL_ERR("invalid vsid:%x or call_state:%d",
vsid, call_state);
ret = -EINVAL;
goto done;
}
}
err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TTY_MODE, c_value, sizeof(c_value));
if (err >= 0) {
if (strcmp(c_value, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0)
tty_mode = PAL_TTY_OFF;
else if (strcmp(c_value, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0)
tty_mode = PAL_TTY_VCO;
else if (strcmp(c_value, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0)
tty_mode = PAL_TTY_HCO;
else if (strcmp(c_value, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0)
tty_mode = PAL_TTY_FULL;
else {
ret = -EINVAL;
goto done;
}
for ( i = 0; i < max_voice_sessions_; i++) {
voice_.session[i].tty_mode = tty_mode;
if (IsCallActive(&voice_.session[i])) {
params = (pal_param_payload *)calloc(1,
sizeof(pal_param_payload) + sizeof(tty_mode));
if (!params) {
AHAL_ERR("calloc failed for size %zu",
sizeof(pal_param_payload) + sizeof(tty_mode));
continue;
}
params->payload_size = sizeof(tty_mode);
memcpy(params->payload, &tty_mode, params->payload_size);
pal_stream_set_param(voice_.session[i].pal_voice_handle,
PAL_PARAM_ID_TTY_MODE, params);
free(params);
params = nullptr;
/*need to device switch for hco and vco*/
if (tty_mode == PAL_TTY_VCO || tty_mode == PAL_TTY_HCO) {
VoiceSetDevice(&voice_.session[i]);
}
}
}
}
err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOLUME_BOOST, c_value, sizeof(c_value));
if (err >= 0) {
if (strcmp(c_value, "on") == 0)
volume_boost = true;
else if (strcmp(c_value, "off") == 0) {
volume_boost = false;
}
else {
ret = -EINVAL;
goto done;
}
params = (pal_param_payload *)calloc(1, sizeof(pal_param_payload) +
sizeof(volume_boost));
if (!params) {
AHAL_ERR("calloc failed for size %zu",
sizeof(pal_param_payload) + sizeof(volume_boost));
} else {
params->payload_size = sizeof(volume_boost);
params->payload[0] = volume_boost;
for ( i = 0; i < max_voice_sessions_; i++) {
voice_.session[i].volume_boost = volume_boost;
if (IsCallActive(&voice_.session[i])) {
pal_stream_set_param(voice_.session[i].pal_voice_handle,
PAL_PARAM_ID_VOLUME_BOOST, params);
}
}
free(params);
params = nullptr;
}
}
err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_SLOWTALK, c_value, sizeof(c_value));
if (err >= 0) {
if (strcmp(c_value, "true") == 0)
slow_talk = true;
else if (strcmp(c_value, "false") == 0) {
slow_talk = false;
}
else {
ret = -EINVAL;
goto done;
}
params = (pal_param_payload *)calloc(1, sizeof(pal_param_payload) +
sizeof(slow_talk));
if (!params) {
AHAL_ERR("calloc failed for size %zu",
sizeof(pal_param_payload) + sizeof(slow_talk));
} else {
params->payload_size = sizeof(slow_talk);
params->payload[0] = slow_talk;
for ( i = 0; i < max_voice_sessions_; i++) {
voice_.session[i].slow_talk = slow_talk;
if (IsCallActive(&voice_.session[i])) {
pal_stream_set_param(voice_.session[i].pal_voice_handle,
PAL_PARAM_ID_SLOW_TALK, params);
}
}
free(params);
params = nullptr;
}
}
err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HD_VOICE, c_value, sizeof(c_value));
if (err >= 0) {
if (strcmp(c_value, "true") == 0)
hd_voice = true;
else if (strcmp(c_value, "false") == 0) {
hd_voice = false;
}
else {
ret = -EINVAL;
goto done;
}
params = (pal_param_payload *)calloc(1, sizeof(pal_param_payload) +
sizeof(hd_voice));
if (!params) {
AHAL_ERR("calloc failed for size %zu",
sizeof(pal_param_payload) + sizeof(hd_voice));
} else {
params->payload_size = sizeof(hd_voice);
params->payload[0] = hd_voice;
for ( i = 0; i < max_voice_sessions_; i++) {
voice_.session[i].hd_voice = hd_voice;
if (IsCallActive(&voice_.session[i])) {
pal_stream_set_param(voice_.session[i].pal_voice_handle,
PAL_PARAM_ID_HD_VOICE, params);
}
}
free(params);
params = nullptr;
}
}
err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DEVICE_MUTE, c_value,
sizeof(c_value));
if (err >= 0) {
bool mute = false;
pal_stream_direction_t dir = PAL_AUDIO_INPUT;
str_parms_del(parms, AUDIO_PARAMETER_KEY_DEVICE_MUTE);
if (strcmp(c_value, "true") == 0) {
mute = true;
}
err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DIRECTION, c_value,
sizeof(c_value));
if (err >= 0) {
str_parms_del(parms, AUDIO_PARAMETER_KEY_DIRECTION);
if (strcmp(c_value, "rx") == 0){
dir = PAL_AUDIO_OUTPUT;
}
} else {
AHAL_ERR("error direction key not found");
ret = -EINVAL;
goto done;
}
params = (pal_param_payload *)calloc(1, sizeof(pal_param_payload) +
sizeof(pal_device_mute_t));
if (!params) {
AHAL_ERR("calloc failed for size %zu",
sizeof(pal_param_payload) + sizeof(pal_device_mute_t));
} else {
params->payload_size = sizeof(pal_device_mute_t);
for ( i = 0; i < max_voice_sessions_; i++) {
voice_.session[i].device_mute.mute = mute;
voice_.session[i].device_mute.dir = dir;
memcpy(params->payload, &(voice_.session[i].device_mute), params->payload_size);
if (IsCallActive(&voice_.session[i])) {
ret= pal_stream_set_param(voice_.session[i].pal_voice_handle,
PAL_PARAM_ID_DEVICE_MUTE, params);
}
if (ret != 0) {
AHAL_ERR("Failed to set mute err:%d", ret);
ret = -EINVAL;
goto done;
}
}
}
}
err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HAC, c_value, sizeof(c_value));
if (err >= 0) {
hac = false;
if (strcmp(c_value, AUDIO_PARAMETER_VALUE_HAC_ON) == 0)
hac = true;
for ( i = 0; i < max_voice_sessions_; i++) {
if (voice_.session[i].hac != hac) {
voice_.session[i].hac = hac;
if (IsCallActive(&voice_.session[i])) {
ret = VoiceSetDevice(&voice_.session[i]);
}
}
}
}
done:
str_parms_destroy(parms);
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
void AudioVoice::VoiceGetParameters(struct str_parms *query, struct str_parms *reply)
{
uint32_t tty_mode = 0;
int ret = 0;
char value[256]={0};
ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_TTY_MODE,
value, sizeof(value));
if (ret >= 0) {
for (int voiceSession_ind = 0; voiceSession_ind < max_voice_sessions_; voiceSession_ind++) {
tty_mode = voice_.session[voiceSession_ind].tty_mode;
}
if (tty_mode >= PAL_TTY_OFF || tty_mode <= PAL_TTY_FULL) {
switch(tty_mode) {
case PAL_TTY_OFF:
str_parms_add_str(reply, AUDIO_PARAMETER_KEY_TTY_MODE, AUDIO_PARAMETER_VALUE_TTY_OFF);
break;
case PAL_TTY_VCO:
str_parms_add_str(reply, AUDIO_PARAMETER_KEY_TTY_MODE, AUDIO_PARAMETER_VALUE_TTY_VCO);
break;
case PAL_TTY_HCO:
str_parms_add_str(reply, AUDIO_PARAMETER_KEY_TTY_MODE, AUDIO_PARAMETER_VALUE_TTY_HCO);
break;
case PAL_TTY_FULL:
str_parms_add_str(reply, AUDIO_PARAMETER_KEY_TTY_MODE, AUDIO_PARAMETER_VALUE_TTY_FULL);
break;
}
} else {
AHAL_ERR("Error happened for getting TTY mode");
}
}
return;
}
bool AudioVoice::is_valid_vsid(uint32_t vsid)
{
if (vsid == VOICEMMODE1_VSID ||
vsid == VOICEMMODE2_VSID)
return true;
else
return false;
}
bool AudioVoice::is_valid_call_state(int call_state)
{
if (call_state < CALL_INACTIVE || call_state > CALL_ACTIVE)
return false;
else
return true;
}
int AudioVoice::GetMatchingTxDevices(const std::set<audio_devices_t>& rx_devices,
std::set<audio_devices_t>& tx_devices){
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
for(auto rx_dev : rx_devices)
switch(rx_dev) {
case AUDIO_DEVICE_OUT_EARPIECE:
tx_devices.insert(AUDIO_DEVICE_IN_BUILTIN_MIC);
break;
case AUDIO_DEVICE_OUT_SPEAKER:
tx_devices.insert(AUDIO_DEVICE_IN_BACK_MIC);
break;
case AUDIO_DEVICE_OUT_WIRED_HEADSET:
tx_devices.insert(AUDIO_DEVICE_IN_WIRED_HEADSET);
break;
case AUDIO_DEVICE_OUT_LINE:
case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
tx_devices.insert(AUDIO_DEVICE_IN_BUILTIN_MIC);
break;
case AUDIO_DEVICE_OUT_USB_HEADSET:
case AUDIO_DEVICE_OUT_USB_DEVICE:
if (adevice->usb_input_dev_enabled)
tx_devices.insert(AUDIO_DEVICE_IN_USB_HEADSET);
else
tx_devices.insert(AUDIO_DEVICE_IN_BUILTIN_MIC);
break;
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
tx_devices.insert(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET);
break;
case AUDIO_DEVICE_OUT_HEARING_AID:
tx_devices.insert(AUDIO_DEVICE_IN_BUILTIN_MIC);
break;
default:
tx_devices.insert(AUDIO_DEVICE_NONE);
AHAL_ERR("unsupported Device Id of %d", rx_dev);
break;
}
return tx_devices.size();
}
int AudioVoice::RouteStream(const std::set<audio_devices_t>& rx_devices) {
int ret = 0;
std::set<audio_devices_t> tx_devices;
pal_device_id_t pal_rx_device = (pal_device_id_t) NULL;
pal_device_id_t pal_tx_device = (pal_device_id_t) NULL;
pal_device_id_t* pal_device_ids = NULL;
uint16_t device_count = 0;
AHAL_DBG("Enter");
if (AudioExtn::audio_devices_empty(rx_devices)){
AHAL_ERR("invalid routing device %d", AudioExtn::get_device_types(rx_devices));
goto exit;
}
GetMatchingTxDevices(rx_devices, tx_devices);
/**
* if device_none is in Tx/Rx devices,
* which is invalid, teardown the usecase.
*/
if (tx_devices.find(AUDIO_DEVICE_NONE) != tx_devices.end() ||
rx_devices.find(AUDIO_DEVICE_NONE) != rx_devices.end()) {
AHAL_ERR("Invalid Tx/Rx device");
ret = 0;
goto exit;
}
device_count = tx_devices.size() > rx_devices.size() ? tx_devices.size() : rx_devices.size();
pal_device_ids = (pal_device_id_t *)calloc(1, device_count * sizeof(pal_device_id_t));
if (!pal_device_ids) {
AHAL_ERR("fail to allocate memory for pal device array");
ret = -ENOMEM;
goto exit;
}
AHAL_DBG("Routing is %d", AudioExtn::get_device_types(rx_devices));
if (stream_out_primary_) {
stream_out_primary_->getPalDeviceIds(rx_devices, pal_device_ids);
pal_rx_device = pal_device_ids[0];
memset(pal_device_ids, 0, device_count * sizeof(pal_device_id_t));
stream_out_primary_->getPalDeviceIds(tx_devices, pal_device_ids);
pal_tx_device = pal_device_ids[0];
}
pal_voice_rx_device_id_ = pal_rx_device;
pal_voice_tx_device_id_ = pal_tx_device;
voice_mutex_.lock();
if (!IsAnyCallActive()) {
if (mode_ == AUDIO_MODE_IN_CALL || mode_ == AUDIO_MODE_CALL_SCREEN) {
voice_.in_call = true;
ret = UpdateCalls(voice_.session);
}
} else {
//do device switch here
for (int i = 0; i < max_voice_sessions_; i++) {
ret = VoiceSetDevice(&voice_.session[i]);
if (ret)
AHAL_ERR("Device switch failed for session[%d]", i);
}
}
voice_mutex_.unlock();
free(pal_device_ids);
exit:
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int AudioVoice::UpdateCallState(uint32_t vsid, int call_state) {
voice_session_t *session = NULL;
int i, ret = 0;
bool is_call_active;
for (i = 0; i < max_voice_sessions_; i++) {
if (vsid == voice_.session[i].vsid) {
session = &voice_.session[i];
break;
}
}
voice_mutex_.lock();
if (session) {
session->state.new_ = call_state;
is_call_active = IsCallActive(session);
AHAL_DBG("is_call_active:%d in_call:%d, mode:%d",
is_call_active, voice_.in_call, mode_);
if (is_call_active ||
(voice_.in_call && (mode_ == AUDIO_MODE_IN_CALL || mode_ == AUDIO_MODE_CALL_SCREEN))) {
ret = UpdateCalls(voice_.session);
}
} else {
ret = -EINVAL;
}
voice_mutex_.unlock();
return ret;
}
int AudioVoice::UpdateCalls(voice_session_t *pSession) {
int i, ret = 0;
voice_session_t *session = NULL;
for (i = 0; i < max_voice_sessions_; i++) {
session = &pSession[i];
AHAL_DBG("cur_state=%d new_state=%d vsid=%x",
session->state.current_, session->state.new_, session->vsid);
switch(session->state.new_)
{
case CALL_ACTIVE:
switch(session->state.current_)
{
case CALL_INACTIVE:
AHAL_DBG("INACTIVE -> ACTIVE vsid:%x", session->vsid);
ret = VoiceStart(session);
if (ret < 0) {
AHAL_ERR("VoiceStart() failed");
} else {
session->state.current_ = session->state.new_;
}
break;
default:
AHAL_ERR("CALL_ACTIVE cannot be handled in state=%d vsid:%x",
session->state.current_, session->vsid);
break;
}
break;
case CALL_INACTIVE:
switch(session->state.current_)
{
case CALL_ACTIVE:
AHAL_DBG("ACTIVE -> INACTIVE vsid:%x", session->vsid);
ret = VoiceStop(session);
if (ret < 0) {
AHAL_ERR("VoiceStop() failed");
} else {
session->state.current_ = session->state.new_;
}
break;
default:
AHAL_ERR("CALL_INACTIVE cannot be handled in state=%d vsid:%x",
session->state.current_, session->vsid);
break;
}
break;
default:
break;
} //end out switch loop
} //end for loop
return ret;
}
int AudioVoice::StopCall() {
int i, ret = 0;
AHAL_DBG("Enter");
voice_.in_call = false;
for (i = 0; i < max_voice_sessions_; i++)
voice_.session[i].state.new_ = CALL_INACTIVE;
ret = UpdateCalls(voice_.session);
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
bool AudioVoice::IsCallActive(AudioVoice::voice_session_t *pSession) {
return (pSession->state.current_ != CALL_INACTIVE) ? true : false;
}
bool AudioVoice::IsAnyCallActive()
{
int i;
for (i = 0; i < max_voice_sessions_; i++) {
if (IsCallActive(&voice_.session[i]))
return true;
}
return false;
}
int AudioVoice::VoiceStart(voice_session_t *session) {
int ret;
struct pal_stream_attributes streamAttributes;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
struct pal_device palDevices[2];
struct pal_channel_info out_ch_info = {0, {0}}, in_ch_info = {0, {0}};
pal_param_payload *param_payload = nullptr;
if (!session) {
AHAL_ERR("Invalid session");
return -EINVAL;
}
AHAL_DBG("Enter");
in_ch_info.channels = 1;
in_ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
out_ch_info.channels = 2;
out_ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
out_ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
palDevices[0].id = pal_voice_tx_device_id_;
palDevices[0].config.ch_info = in_ch_info;
palDevices[0].config.sample_rate = 48000;
palDevices[0].config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
palDevices[0].config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE; // TODO: need to convert this from output format
palDevices[0].address.card_id = adevice->usb_card_id_;
palDevices[0].address.device_num =adevice->usb_dev_num_;
palDevices[1].id = pal_voice_rx_device_id_;
palDevices[1].config.ch_info = out_ch_info;
palDevices[1].config.sample_rate = 48000;
palDevices[1].config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
palDevices[1].config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE; // TODO: need to convert this from output format
palDevices[1].address.card_id = adevice->usb_card_id_;
palDevices[1].address.device_num = adevice->usb_dev_num_;
memset(&streamAttributes, 0, sizeof(streamAttributes));
streamAttributes.type = PAL_STREAM_VOICE_CALL;
streamAttributes.info.voice_call_info.VSID = session->vsid;
streamAttributes.info.voice_call_info.tty_mode = session->tty_mode;
/*device overrides for specific use cases*/
if (mode_ == AUDIO_MODE_CALL_SCREEN) {
AHAL_DBG("in call screen mode");
palDevices[0].id = PAL_DEVICE_IN_PROXY; //overwrite the device with proxy dev
palDevices[1].id = PAL_DEVICE_OUT_PROXY; //overwrite the device with proxy dev
}
if (streamAttributes.info.voice_call_info.tty_mode == PAL_TTY_HCO) {
/** device pairs for HCO usecase
* <handset, headset-mic>
* <speaker, headset-mic>
* override devices accordingly.
*/
if (pal_voice_rx_device_id_ == PAL_DEVICE_OUT_WIRED_HEADSET)
palDevices[1].id = PAL_DEVICE_OUT_HANDSET;
else if (pal_voice_rx_device_id_ == PAL_DEVICE_OUT_SPEAKER)
palDevices[0].id = PAL_DEVICE_IN_WIRED_HEADSET;
else
AHAL_ERR("Invalid device pair for the usecase");
}
if (streamAttributes.info.voice_call_info.tty_mode == PAL_TTY_VCO) {
/** device pairs for VCO usecase
* <headphones, handset-mic>
* <headphones, speaker-mic>
* override devices accordingly.
*/
if (pal_voice_rx_device_id_ == PAL_DEVICE_OUT_WIRED_HEADSET ||
pal_voice_rx_device_id_ == PAL_DEVICE_OUT_WIRED_HEADPHONE)
palDevices[0].id = PAL_DEVICE_IN_HANDSET_MIC;
else if (pal_voice_rx_device_id_ == PAL_DEVICE_OUT_SPEAKER)
palDevices[1].id = PAL_DEVICE_OUT_WIRED_HEADSET;
else
AHAL_ERR("Invalid device pair for the usecase");
}
streamAttributes.direction = PAL_AUDIO_INPUT_OUTPUT;
streamAttributes.in_media_config.sample_rate = 48000;
streamAttributes.in_media_config.ch_info = in_ch_info;
streamAttributes.in_media_config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
streamAttributes.in_media_config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE; // TODO: need to convert this from output format
streamAttributes.out_media_config.sample_rate = 48000;
streamAttributes.out_media_config.ch_info = out_ch_info;
streamAttributes.out_media_config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
streamAttributes.out_media_config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE; // TODO: need to convert this from output format
/*set custom key for hac mode*/
if (session && session->hac && palDevices[1].id ==
PAL_DEVICE_OUT_HANDSET) {
strlcat(palDevices[0].custom_config.custom_key, "HAC;",
sizeof(palDevices[0].custom_config.custom_key));
strlcat(palDevices[1].custom_config.custom_key, "HAC;",
sizeof(palDevices[1].custom_config.custom_key));
AHAL_INFO("Setting custom key as %s", palDevices[0].custom_config.custom_key);
}
//streamAttributes.in_media_config.ch_info = ch_info;
ret = pal_stream_open(&streamAttributes,
2,
palDevices,
0,
NULL,
NULL,//callback
(uint64_t)this,
&session->pal_voice_handle);// Need to add this to the audio stream structure.
AHAL_DBG("pal_stream_open() ret:%d", ret);
if (ret) {
AHAL_ERR("Pal Stream Open Error (%x)", ret);
ret = -EINVAL;
goto error_open;
}
/*apply cached voice effects features*/
if (session->slow_talk) {
param_payload = (pal_param_payload *)calloc(1, sizeof(pal_param_payload) +
sizeof(session->slow_talk));
if (!param_payload) {
AHAL_ERR("calloc for size %zu failed",
sizeof(pal_param_payload) + sizeof(session->slow_talk));
} else {
param_payload->payload_size = sizeof(session->slow_talk);
param_payload->payload[0] = session->slow_talk;
ret = pal_stream_set_param(session->pal_voice_handle,
PAL_PARAM_ID_SLOW_TALK,
param_payload);
if (ret)
AHAL_ERR("Slow Talk enable failed %x", ret);
free(param_payload);
param_payload = nullptr;
}
}
if (session->volume_boost) {
param_payload = (pal_param_payload *)calloc(1, sizeof(pal_param_payload) +
sizeof(session->volume_boost));
if (!param_payload) {
AHAL_ERR("calloc for size %zu failed",
sizeof(pal_param_payload) + sizeof(session->volume_boost));
} else {
param_payload->payload_size = sizeof(session->volume_boost);
param_payload->payload[0] = session->volume_boost;
ret = pal_stream_set_param(session->pal_voice_handle, PAL_PARAM_ID_VOLUME_BOOST,
param_payload);
if (ret)
AHAL_ERR("Volume Boost enable failed %x", ret);
free(param_payload);
param_payload = nullptr;
}
}
if (session->hd_voice) {
param_payload = (pal_param_payload *)calloc(1, sizeof(pal_param_payload) +
sizeof(session->hd_voice));
if (!param_payload) {
AHAL_ERR("calloc for size %zu failed",
sizeof(pal_param_payload) + sizeof(session->hd_voice));
} else {
param_payload->payload_size = sizeof(session->hd_voice);
param_payload->payload[0] = session->hd_voice;
ret = pal_stream_set_param(session->pal_voice_handle, PAL_PARAM_ID_HD_VOICE,
param_payload);
if (ret)
AHAL_ERR("HD Voice enable failed %x",ret);
free(param_payload);
param_payload = nullptr;
}
}
/* apply cached volume set by APM */
if (session->pal_voice_handle && session->pal_vol_data &&
session->pal_vol_data->volume_pair[0].vol != -1.0) {
ret = pal_stream_set_volume(session->pal_voice_handle, session->pal_vol_data);
if (ret)
AHAL_ERR("Failed to apply volume on voice session %x", ret);
} else {
if (!session->pal_voice_handle || !session->pal_vol_data)
AHAL_ERR("Invalid voice handle or volume data");
if (session->pal_vol_data && session->pal_vol_data->volume_pair[0].vol == -1.0)
AHAL_DBG("session volume is not set");
}
ret = pal_stream_start(session->pal_voice_handle);
if (ret) {
AHAL_ERR("Pal Stream Start Error (%x)", ret);
ret = pal_stream_close(session->pal_voice_handle);
if (ret)
AHAL_ERR("Pal Stream close failed %x", ret);
session->pal_voice_handle = NULL;
ret = -EINVAL;
} else {
AHAL_DBG("Pal Stream Start Success");
}
/*Apply device mute if needed*/
if (session->device_mute.mute) {
ret = SetDeviceMute(session);
}
/*apply cached mic mute*/
if (adevice->mute_) {
pal_stream_set_mute(session->pal_voice_handle, adevice->mute_);
}
error_open:
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int AudioVoice::SetDeviceMute(voice_session_t *session) {
int ret = 0;
pal_param_payload *param_payload = nullptr;
if (!session) {
AHAL_ERR("Invalid Session");
return -EINVAL;
}
param_payload = (pal_param_payload *)calloc(1, sizeof(pal_param_payload) +
sizeof(session->device_mute));
if (!param_payload) {
AHAL_ERR("calloc failed for size %zu",
sizeof(pal_param_payload) + sizeof(session->device_mute));
ret = -EINVAL;
} else {
param_payload->payload_size = sizeof(session->device_mute);
memcpy(param_payload->payload, &(session->device_mute), param_payload->payload_size);
ret = pal_stream_set_param(session->pal_voice_handle, PAL_PARAM_ID_DEVICE_MUTE,
param_payload);
if (ret)
AHAL_ERR("Voice Device mute failed %x", ret);
free(param_payload);
param_payload = nullptr;
}
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int AudioVoice::VoiceStop(voice_session_t *session) {
int ret = 0;
AHAL_DBG("Enter");
if (session && session->pal_voice_handle) {
ret = pal_stream_stop(session->pal_voice_handle);
if (ret)
AHAL_ERR("Pal Stream stop failed %x", ret);
ret = pal_stream_close(session->pal_voice_handle);
if (ret)
AHAL_ERR("Pal Stream close failed %x", ret);
session->pal_voice_handle = NULL;
}
if (ret)
ret = -EINVAL;
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int AudioVoice::VoiceSetDevice(voice_session_t *session) {
int ret = 0;
struct pal_device palDevices[2];
struct pal_channel_info out_ch_info = {0, {0}}, in_ch_info = {0, {0}};
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
pal_param_payload *param_payload = nullptr;
if (!session) {
AHAL_ERR("Invalid session");
return -EINVAL;
}
AHAL_DBG("Enter");
in_ch_info.channels = 1;
in_ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
out_ch_info.channels = 2;
out_ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
out_ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
palDevices[0].id = pal_voice_tx_device_id_;
palDevices[0].config.ch_info = in_ch_info;
palDevices[0].config.sample_rate = 48000;
palDevices[0].config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
palDevices[0].config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE; // TODO: need to convert this from output format
palDevices[0].address.card_id = adevice->usb_card_id_;
palDevices[0].address.device_num =adevice->usb_dev_num_;
palDevices[1].id = pal_voice_rx_device_id_;
palDevices[1].config.ch_info = out_ch_info;
palDevices[1].config.sample_rate = 48000;
palDevices[1].config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
palDevices[1].config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE; // TODO: need to convert this from output format
palDevices[1].address.card_id = adevice->usb_card_id_;
palDevices[1].address.device_num =adevice->usb_dev_num_;
/*device overwrites for usecases*/
if (mode_ == AUDIO_MODE_CALL_SCREEN) {
AHAL_DBG("in call screen mode");
palDevices[0].id = PAL_DEVICE_IN_PROXY; //overwrite the device with proxy dev
palDevices[1].id = PAL_DEVICE_OUT_PROXY; //overwrite the device with proxy dev
}
if (session && session->tty_mode == PAL_TTY_HCO) {
/** device pairs for HCO usecase
* <handset, headset-mic>
* <speaker, headset-mic>
* override devices accordingly.
*/
if (pal_voice_rx_device_id_ == PAL_DEVICE_OUT_WIRED_HEADSET)
palDevices[1].id = PAL_DEVICE_OUT_HANDSET;
else if (pal_voice_rx_device_id_ == PAL_DEVICE_OUT_SPEAKER)
palDevices[0].id = PAL_DEVICE_IN_WIRED_HEADSET;
else
AHAL_ERR("Invalid device pair for the usecase");
}
if (session && session->tty_mode == PAL_TTY_VCO) {
/** device pairs for VCO usecase
* <headphones, handset-mic>
* <headphones, speaker-mic>
* override devices accordingly.
*/
if (pal_voice_rx_device_id_ == PAL_DEVICE_OUT_WIRED_HEADSET ||
pal_voice_rx_device_id_ == PAL_DEVICE_OUT_WIRED_HEADPHONE)
palDevices[0].id = PAL_DEVICE_IN_HANDSET_MIC;
else if (pal_voice_rx_device_id_ == PAL_DEVICE_OUT_SPEAKER)
palDevices[1].id = PAL_DEVICE_OUT_WIRED_HEADSET;
else
AHAL_ERR("Invalid device pair for the usecase");
}
if (session && session->volume_boost) {
/* volume boost if device is not supported */
param_payload = (pal_param_payload *)calloc(1, sizeof(pal_param_payload) +
sizeof(session->volume_boost));
if (!param_payload) {
AHAL_ERR("calloc for size %zu failed",
sizeof(pal_param_payload) + sizeof(session->volume_boost));
} else {
param_payload->payload_size = sizeof(session->volume_boost);
if (palDevices[1].id != PAL_DEVICE_OUT_HANDSET &&
palDevices[1].id != PAL_DEVICE_OUT_SPEAKER)
param_payload->payload[0] = false;
else
param_payload->payload[0] = true;
ret = pal_stream_set_param(session->pal_voice_handle, PAL_PARAM_ID_VOLUME_BOOST,
param_payload);
if (ret)
AHAL_ERR("Volume Boost enable/disable failed %x", ret);
free(param_payload);
param_payload = nullptr;
}
}
/*set or remove custom key for hac mode*/
if (session && session->hac && palDevices[1].id ==
PAL_DEVICE_OUT_HANDSET) {
strlcat(palDevices[0].custom_config.custom_key, "HAC;",
sizeof(palDevices[0].custom_config.custom_key));
strlcat(palDevices[1].custom_config.custom_key, "HAC;",
sizeof(palDevices[1].custom_config.custom_key));
AHAL_INFO("Setting custom key as %s", palDevices[0].custom_config.custom_key);
} else {
strlcpy(palDevices[0].custom_config.custom_key, "",
sizeof(palDevices[0].custom_config.custom_key));
}
if (session && session->pal_voice_handle) {
ret = pal_stream_set_device(session->pal_voice_handle, 2, palDevices);
if (ret) {
AHAL_ERR("Pal Stream Set Device failed %x", ret);
ret = -EINVAL;
goto exit;
}
} else {
AHAL_ERR("Voice handle not found");
}
/* apply device mute if needed*/
if (session->device_mute.mute) {
ret = SetDeviceMute(session);
}
exit:
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int AudioVoice::SetMicMute(bool mute) {
int ret = 0;
voice_session_t *session = voice_.session;
AHAL_DBG("Enter mute: %d", mute);
if (session) {
for (int i = 0; i < max_voice_sessions_; i++) {
if (session[i].pal_voice_handle) {
ret = pal_stream_set_mute(session[i].pal_voice_handle, mute);
if (ret)
AHAL_ERR("Error applying mute %d for voice session %d", mute, i);
}
}
}
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int AudioVoice::SetVoiceVolume(float volume) {
int ret = 0;
voice_session_t *session = voice_.session;
AHAL_DBG("Enter vol: %f", volume);
if (session) {
for (int i = 0; i < max_voice_sessions_; i++) {
/* APM volume is cached when voice call is not active
* cached volume is applied in voicestart before pal_stream_start
*/
if (session[i].pal_vol_data) {
session[i].pal_vol_data->volume_pair[0].vol = volume;
if (session[i].pal_voice_handle) {
ret = pal_stream_set_volume(session[i].pal_voice_handle,
session[i].pal_vol_data);
AHAL_DBG("volume applied on voice session %d status %x", i, ret);
} else {
AHAL_DBG("volume is cached on voice session %d", i);
}
} else {
AHAL_ERR("unable to apply/cache volume on voice session %d", i);
}
}
}
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
AudioVoice::AudioVoice() {
voice_.in_call = false;
max_voice_sessions_ = MAX_VOICE_SESSIONS;
pal_vol_ = NULL;
pal_vol_ = (struct pal_volume_data*)malloc(sizeof(uint32_t)
+ sizeof(struct pal_channel_vol_kv));
if (pal_vol_) {
pal_vol_->no_of_volpair = 1;
pal_vol_->volume_pair[0].channel_mask = 0x01;
pal_vol_->volume_pair[0].vol = -1.0;
} else {
AHAL_ERR("volume malloc failed %s", strerror(errno));
}
for (int i = 0; i < max_voice_sessions_; i++) {
voice_.session[i].state.current_ = CALL_INACTIVE;
voice_.session[i].state.new_ = CALL_INACTIVE;
voice_.session[i].vsid = VOICEMMODE1_VSID;
voice_.session[i].pal_voice_handle = NULL;
voice_.session[i].tty_mode = PAL_TTY_OFF;
voice_.session[i].volume_boost = false;
voice_.session[i].slow_talk = false;
voice_.session[i].pal_voice_handle = NULL;
voice_.session[i].hd_voice = false;
voice_.session[i].pal_vol_data = pal_vol_;
voice_.session[i].device_mute.dir = PAL_AUDIO_OUTPUT;
voice_.session[i].device_mute.mute = false;
voice_.session[i].hac = false;
}
voice_.session[MMODE1_SESS_IDX].vsid = VOICEMMODE1_VSID;
voice_.session[MMODE2_SESS_IDX].vsid = VOICEMMODE2_VSID;
stream_out_primary_ = NULL;
}
AudioVoice::~AudioVoice() {
voice_.in_call = false;
if (pal_vol_)
free(pal_vol_);
for (int i = 0; i < max_voice_sessions_; i++) {
voice_.session[i].state.current_ = CALL_INACTIVE;
voice_.session[i].state.new_ = CALL_INACTIVE;
voice_.session[i].vsid = VOICEMMODE1_VSID;
voice_.session[i].tty_mode = PAL_TTY_OFF;
voice_.session[i].volume_boost = false;
voice_.session[i].slow_talk = false;
voice_.session[i].pal_voice_handle = NULL;
voice_.session[i].hd_voice = false;
voice_.session[i].pal_vol_data = NULL;
voice_.session[i].hac = false;
}
voice_.session[MMODE1_SESS_IDX].vsid = VOICEMMODE1_VSID;
voice_.session[MMODE2_SESS_IDX].vsid = VOICEMMODE2_VSID;
stream_out_primary_ = NULL;
max_voice_sessions_ = 0;
}