blob: e8393ff53644a826c37b2514bd7d92fbc629764d [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_NDEBUG 0
#define LOG_TAG "AHAL: AudioStream"
#define ATRACE_TAG (ATRACE_TAG_AUDIO | ATRACE_TAG_HAL)
#include "AudioCommon.h"
#include "AudioDevice.h"
#include "AudioStream.h"
#include <log/log.h>
#include <utils/Trace.h>
#include <cutils/properties.h>
#include <inttypes.h>
#include <chrono>
#include <thread>
#include "PalApi.h"
#include <audio_effects/effect_aec.h>
#include <audio_effects/effect_ns.h>
#include "audio_extn.h"
#include <audio_utils/format.h>
#define COMPRESS_OFFLOAD_FRAGMENT_SIZE (32 * 1024)
#define FLAC_COMPRESS_OFFLOAD_FRAGMENT_SIZE (256 * 1024)
#define COMPRESS_CAPTURE_AAC_MAX_OUTPUT_BUFFER_SIZE 2048
#define MAX_READ_RETRY_COUNT 25
#define MAX_ACTIVE_MICROPHONES_TO_SUPPORT 10
#define AFE_PROXY_RECORD_PERIOD_SIZE 768
static bool karaoke = false;
static bool is_pcm_format(audio_format_t format)
{
if (format == AUDIO_FORMAT_PCM_16_BIT ||
format == AUDIO_FORMAT_PCM_8_BIT ||
format == AUDIO_FORMAT_PCM_8_24_BIT ||
format == AUDIO_FORMAT_PCM_FLOAT ||
format == AUDIO_FORMAT_PCM_24_BIT_PACKED ||
format == AUDIO_FORMAT_PCM_32_BIT)
return true;
return false;
}
static bool is_hdr_mode_enabled() {
if (!property_get_bool("vendor.audio.hdr.record.enable", false)) {
AHAL_INFO("HDR feature is disabled");
return false;
}
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
return adevice->hdr_record_enabled;
}
static void setup_hdr_usecase(struct pal_device* palInDevice) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
bool orientationLandscape = adevice->orientation_landscape;
bool orientationInverted = adevice->inverted;
if (orientationLandscape && !orientationInverted) {
strlcat(palInDevice->custom_config.custom_key,
"unprocessed-hdr-mic-landscape;",
sizeof(palInDevice->custom_config.custom_key));
} else if (!orientationLandscape && !orientationInverted) {
strlcat(palInDevice->custom_config.custom_key,
"unprocessed-hdr-mic-portrait;",
sizeof(palInDevice->custom_config.custom_key));
} else if (orientationLandscape && orientationInverted) {
strlcat(palInDevice->custom_config.custom_key,
"unprocessed-hdr-mic-inverted-landscape;",
sizeof(palInDevice->custom_config.custom_key));
} else if (!orientationLandscape && orientationInverted) {
strlcat(palInDevice->custom_config.custom_key,
"unprocessed-hdr-mic-inverted-portrait;",
sizeof(palInDevice->custom_config.custom_key));
}
AHAL_INFO("Setting custom key as %s",
palInDevice->custom_config.custom_key);
}
/*
* Scope based implementation of acquiring/releasing PerfLock.
*/
class AutoPerfLock {
public :
AutoPerfLock() {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
if (adevice) {
adevice->adev_perf_mutex.lock();
++adevice->perf_lock_acquire_cnt;
if (adevice->perf_lock_acquire_cnt == 1)
AudioExtn::audio_extn_perf_lock_acquire(&adevice->perf_lock_handle, 0,
adevice->perf_lock_opts, adevice->perf_lock_opts_size);
AHAL_DBG("(Acquired) perf_lock_handle: 0x%x, count: %d",
adevice->perf_lock_handle, adevice->perf_lock_acquire_cnt);
adevice->adev_perf_mutex.unlock();
}
}
~AutoPerfLock() {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
if (adevice) {
adevice->adev_perf_mutex.lock();
AHAL_DBG("(release) perf_lock_handle: 0x%x, count: %d",
adevice->perf_lock_handle, adevice->perf_lock_acquire_cnt);
if (adevice->perf_lock_acquire_cnt > 0)
--adevice->perf_lock_acquire_cnt;
if (adevice->perf_lock_acquire_cnt == 0) {
AHAL_DBG("Releasing perf_lock_handle: 0x%x", adevice->perf_lock_handle);
AudioExtn::audio_extn_perf_lock_release(&adevice->perf_lock_handle);
}
adevice->adev_perf_mutex.unlock();
}
}
};
void StreamOutPrimary::GetStreamHandle(audio_stream_out** stream) {
*stream = (audio_stream_out*)stream_.get();
}
void StreamInPrimary::GetStreamHandle(audio_stream_in** stream) {
*stream = (audio_stream_in*)stream_.get();
}
uint32_t StreamPrimary::GetSampleRate() {
return config_.sample_rate;
}
audio_format_t StreamPrimary::GetFormat() {
return config_.format;
}
audio_channel_mask_t StreamPrimary::GetChannelMask() {
return config_.channel_mask;
}
audio_io_handle_t StreamPrimary::GetHandle()
{
return handle_;
}
int StreamPrimary::GetUseCase()
{
return usecase_;
}
bool StreamPrimary::GetSupportedConfig(bool isOutStream,
struct str_parms *query,
struct str_parms *reply)
{
char value[256];
int ret = 0;
bool found = false;
int index = 0;
int table_size = 0;
int i = 0;
ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value, sizeof(value));
if (ret >= 0) {
value[0] = '\0';
int stream_format = 0;
bool first = true;
table_size = sizeof(formats_name_to_enum_table) / sizeof(struct string_to_enum);
if (device_cap_query_) {
while (device_cap_query_->config->format[i]!= 0) {
stream_format = device_cap_query_->config->format[i];
index = GetLookupTableIndex(formats_name_to_enum_table,
table_size, stream_format);
if (!first) {
strlcat(value, "|", sizeof(value));
}
if (index >= 0 && index < table_size) {
strlcat(value, formats_name_to_enum_table[index].name, sizeof(value));
found = true;
first = false;
}
i++;
}
} else {
stream_format = GetFormat();
index = GetLookupTableIndex(formats_name_to_enum_table,
table_size, stream_format);
if (index >= 0 && index < table_size) {
strlcat(value, formats_name_to_enum_table[index].name, sizeof(value));
found = true;
}
}
str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value);
}
ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value, sizeof(value));
if (ret >= 0) {
bool first = true;
value[0] = '\0';
i = 0;
if (device_cap_query_) {
while (device_cap_query_->config->mask[i] != 0) {
for (int j = 0; j < ARRAY_SIZE(channels_name_to_enum_table); j++) {
if (channels_name_to_enum_table[j].value == device_cap_query_->config->mask[i]) {
if (!first)
strlcat(value, "|", sizeof(value));
strlcat(value, channels_name_to_enum_table[j].name, sizeof(value));
first = false;
break;
}
}
i++;
}
} else {
if (isOutStream)
strlcat(value, "AUDIO_CHANNEL_OUT_STEREO", sizeof(value));
else
strlcat(value, "AUDIO_CHANNEL_IN_STEREO", sizeof(value));
}
str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value);
found = true;
}
ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, value, sizeof(value));
if (ret >= 0) {
value[0] = '\0';
i = 0;
int cursor = 0;
if (device_cap_query_) {
while (device_cap_query_->config->sample_rate[i] != 0) {
int avail = sizeof(value) - cursor;
ret = snprintf(value + cursor, avail, "%s%d",
cursor > 0 ? "|" : "",
device_cap_query_->config->sample_rate[i]);
if (ret < 0 || ret >= avail) {
// if cursor is at the last element of the array
// overwrite with \0 is duplicate work as
// snprintf already put a \0 in place.
// else
// we had space to write the '|' at value[cursor]
// (which will be overwritten) or no space to fill
// the first element (=> cursor == 0)
value[cursor] = '\0';
break;
}
cursor += ret;
++i;
}
} else {
int stream_sample_rate = GetSampleRate();
int avail = sizeof(value);
ret = snprintf(value, avail, "%d", stream_sample_rate);
}
str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES,
value);
found = true;
}
return found;
}
#if 0
static pal_stream_type_t GetPalStreamType(audio_output_flags_t flags) {
std::ignore = flags;
return PAL_STREAM_LOW_LATENCY;
}
#endif
//audio_hw_device_t* AudioDevice::device_ = NULL;
std::shared_ptr<AudioDevice> AudioDevice::adev_ = nullptr;
std::shared_ptr<audio_hw_device_t> AudioDevice::device_ = nullptr;
static int32_t pal_callback(pal_stream_handle_t *stream_handle,
uint32_t event_id, uint32_t *event_data,
uint32_t event_size, uint64_t cookie)
{
stream_callback_event_t event;
StreamOutPrimary *astream_out = reinterpret_cast<StreamOutPrimary *> (cookie);
AHAL_VERBOSE("stream_handle (%p), event_id (%x), event_data (%p), cookie %" PRIu64
"event_size (%d)", stream_handle, event_id, event_data,
cookie, event_size);
switch (event_id)
{
case PAL_STREAM_CBK_EVENT_WRITE_READY:
{
std::lock_guard<std::mutex> write_guard (astream_out->write_wait_mutex_);
astream_out->write_ready_ = true;
AHAL_VERBOSE("received WRITE_READY event");
(astream_out->write_condition_).notify_all();
event = STREAM_CBK_EVENT_WRITE_READY;
}
break;
case PAL_STREAM_CBK_EVENT_DRAIN_READY:
{
std::lock_guard<std::mutex> drain_guard (astream_out->drain_wait_mutex_);
astream_out->drain_ready_ = true;
astream_out->sendGaplessMetadata = false;
AHAL_DBG("received DRAIN_READY event");
(astream_out->drain_condition_).notify_all();
event = STREAM_CBK_EVENT_DRAIN_READY;
}
break;
case PAL_STREAM_CBK_EVENT_PARTIAL_DRAIN_READY:
{
std::lock_guard<std::mutex> drain_guard (astream_out->drain_wait_mutex_);
astream_out->drain_ready_ = true;
astream_out->sendGaplessMetadata = true;
AHAL_DBG("received PARTIAL DRAIN_READY event");
(astream_out->drain_condition_).notify_all();
event = STREAM_CBK_EVENT_DRAIN_READY;
}
break;
case PAL_STREAM_CBK_EVENT_ERROR:
AHAL_DBG("received PAL_STREAM_CBK_EVENT_ERROR event");
event = STREAM_CBK_EVENT_ERROR;
break;
default:
AHAL_ERR("Invalid event id:%d", event_id);
return -EINVAL;
}
if (astream_out && astream_out->client_callback) {
AHAL_VERBOSE("Callback to Framework");
astream_out->client_callback(event, NULL, astream_out->client_cookie);
}
return 0;
}
static int astream_out_mmap_noirq_start(const struct audio_stream_out *stream)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
if (!astream_out) {
AHAL_ERR("unable to get audio OutStream");
return -EINVAL;
}
return astream_out->Start();
}
static int astream_out_mmap_noirq_stop(const struct audio_stream_out *stream)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
if (!astream_out) {
AHAL_ERR("unable to get audio OutStream");
return -EINVAL;
}
return astream_out->Stop();
}
static int astream_out_create_mmap_buffer(const struct audio_stream_out *stream,
int32_t min_size_frames, struct audio_mmap_buffer_info *info)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
int ret = 0;
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
if (!astream_out) {
AHAL_ERR("unable to get audio OutStream");
return -EINVAL;
}
if (info == NULL || !(min_size_frames > 0 && min_size_frames < INT32_MAX)) {
AHAL_ERR("invalid field info = %p, min_size_frames = %d", info, min_size_frames);
return -EINVAL;
}
if (astream_out->GetUseCase() != USECASE_AUDIO_PLAYBACK_MMAP) {
AHAL_ERR("invalid usecase = %d", astream_out->GetUseCase());
return -ENOSYS;
}
ret = astream_out->CreateMmapBuffer(min_size_frames, info);
if (ret)
AHAL_ERR("failed %d\n", ret);
return ret;
}
static int astream_out_get_mmap_position(const struct audio_stream_out *stream,
struct audio_mmap_position *position)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
if (!astream_out) {
AHAL_ERR("unable to get audio OutStream");
return -EINVAL;
}
if (astream_out->GetUseCase() != USECASE_AUDIO_PLAYBACK_MMAP) {
AHAL_ERR("invalid usecase = %d", astream_out->GetUseCase());
return -ENOSYS;
}
return astream_out->GetMmapPosition(position);
}
static uint32_t astream_out_get_sample_rate(const struct audio_stream *stream) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (adevice) {
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return 0;
}
if (astream_out)
return astream_out->GetSampleRate();
else
return 0;
}
static int astream_set_sample_rate(struct audio_stream *stream __unused,
uint32_t rate __unused) {
return -ENOSYS;
}
static audio_format_t astream_out_get_format(
const struct audio_stream *stream) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (adevice)
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
else
AHAL_ERR("unable to get audio device");
if (astream_out)
return astream_out->GetFormat();
else
return AUDIO_FORMAT_DEFAULT;
}
static int astream_out_get_next_write_timestamp(
const struct audio_stream_out *stream __unused,
int64_t *timestamp __unused) {
return -ENOSYS;
}
static int astream_set_format(struct audio_stream *stream __unused,
audio_format_t format __unused) {
return -ENOSYS;
}
static size_t astream_out_get_buffer_size(const struct audio_stream *stream) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out =
adevice->OutGetStream((audio_stream_t*)stream);
if (astream_out)
return astream_out->GetBufferSize();
else
return 0;
}
static audio_channel_mask_t astream_out_get_channels(const struct audio_stream *stream) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out = nullptr;
AHAL_VERBOSE("stream_out(%p)", stream);
if (!adevice) {
AHAL_ERR("unable to get audio device");
return (audio_channel_mask_t) 0;
}
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
if (!astream_out) {
AHAL_ERR("unable to get audio stream");
return (audio_channel_mask_t) 0;
}
if (astream_out->GetChannelMask())
return astream_out->GetChannelMask();
return (audio_channel_mask_t) 0;
}
static int astream_pause(struct audio_stream_out *stream)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
if (!astream_out) {
AHAL_ERR("unable to get audio stream");
return -EINVAL;
}
AHAL_DBG("pause");
return astream_out->Pause();
}
static int astream_resume(struct audio_stream_out *stream)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
if (!astream_out) {
AHAL_ERR("unable to get audio stream");
return -EINVAL;
}
return astream_out->Resume();
}
static int astream_flush(struct audio_stream_out *stream)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
if (!astream_out) {
AHAL_ERR("unable to get audio stream");
return -EINVAL;
}
return astream_out->Flush();
}
static int astream_drain(struct audio_stream_out *stream, audio_drain_type_t type)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
if (!astream_out) {
AHAL_ERR("unable to get audio stream");
return -EINVAL;
}
return astream_out->Drain(type);
}
static int astream_set_callback(struct audio_stream_out *stream, stream_callback_t callback, void *cookie)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (!callback) {
AHAL_ERR("error: NULL Callback passed");
return -EINVAL;
}
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
if (!astream_out) {
AHAL_ERR("unable to get audio stream");
return -EINVAL;
}
astream_out->client_callback = callback;
astream_out->client_cookie = cookie;
return 0;
}
static int astream_out_standby(struct audio_stream *stream) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
int ret = 0;
if (adevice) {
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
AHAL_DBG("enter: stream (%p), usecase(%d: %s)", astream_out.get(),
astream_out->GetUseCase(), use_case_table[astream_out->GetUseCase()]);
if (astream_out) {
ret = astream_out->Standby();
} else {
AHAL_ERR("unable to get audio stream");
ret = -EINVAL;
}
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
static int astream_dump(const struct audio_stream *stream, int fd) {
std::ignore = stream;
std::ignore = fd;
AHAL_DBG("dump function not implemented");
return 0;
}
static uint32_t astream_get_latency(const struct audio_stream_out *stream) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
uint32_t period_ms, latency = 0;
int trial = 0;
char value[PROPERTY_VALUE_MAX] = {0};
int low_latency_period_size = LOW_LATENCY_PLAYBACK_PERIOD_SIZE;
if (adevice) {
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
switch (astream_out->GetUseCase()) {
case USECASE_AUDIO_PLAYBACK_OFFLOAD:
//TODO: get dsp latency for compress usecase
latency = COMPRESS_OFFLOAD_PLAYBACK_LATENCY;
break;
case USECASE_AUDIO_PLAYBACK_ULL:
case USECASE_AUDIO_PLAYBACK_MMAP:
period_ms = (ULL_PERIOD_MULTIPLIER * ULL_PERIOD_SIZE *
1000) / DEFAULT_OUTPUT_SAMPLING_RATE;
latency = period_ms +
StreamOutPrimary::GetRenderLatency(astream_out->flags_) / 1000;
break;
case USECASE_AUDIO_PLAYBACK_OFFLOAD2:
latency = PCM_OFFLOAD_OUTPUT_PERIOD_DURATION;
latency += StreamOutPrimary::GetRenderLatency(astream_out->flags_) / 1000;
break;
case USECASE_AUDIO_PLAYBACK_DEEP_BUFFER:
latency = DEEP_BUFFER_OUTPUT_PERIOD_DURATION * DEEP_BUFFER_PLAYBACK_PERIOD_COUNT;
latency += StreamOutPrimary::GetRenderLatency(astream_out->flags_) / 1000;
break;
case USECASE_AUDIO_PLAYBACK_LOW_LATENCY:
if (property_get("vendor.audio_hal.period_size", value, NULL) > 0) {
trial = atoi(value);
if (astream_out->period_size_is_plausible_for_low_latency(trial))
low_latency_period_size = trial;
}
latency = (LOW_LATENCY_PLAYBACK_PERIOD_COUNT * low_latency_period_size * 1000)/ (astream_out->GetSampleRate());
latency += StreamOutPrimary::GetRenderLatency(astream_out->flags_) / 1000;
break;
case USECASE_AUDIO_PLAYBACK_WITH_HAPTICS:
latency = LOW_LATENCY_OUTPUT_PERIOD_DURATION * LOW_LATENCY_PLAYBACK_PERIOD_COUNT;
latency += StreamOutPrimary::GetRenderLatency(astream_out->flags_) / 1000;
break;
case USECASE_AUDIO_PLAYBACK_VOIP:
latency += VOIP_PERIOD_COUNT_DEFAULT * DEFAULT_VOIP_BUF_DURATION_MS;
break;
default:
latency += StreamOutPrimary::GetRenderLatency(astream_out->flags_) / 1000;
break;
}
// accounts for A2DP encoding and sink latency
pal_param_bta2dp_t *param_bt_a2dp = NULL;
size_t size = 0;
int32_t ret;
if (astream_out->isDeviceAvailable(PAL_DEVICE_OUT_BLUETOOTH_A2DP)) {
ret = pal_get_param(PAL_PARAM_ID_BT_A2DP_ENCODER_LATENCY,
(void **)&param_bt_a2dp, &size, nullptr);
if (!ret && param_bt_a2dp)
latency += param_bt_a2dp->latency;
}
AHAL_VERBOSE("Latency %d", latency);
return latency;
}
static int astream_out_get_presentation_position(
const struct audio_stream_out *stream,
uint64_t *frames, struct timespec *timestamp) {
std::ignore = stream;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
int ret = 0;
if (adevice) {
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
if (!timestamp) {
AHAL_ERR("error: timestamp NULL");
return -EINVAL;
}
if (astream_out) {
switch (astream_out->GetPalStreamType(astream_out->flags_)) {
case PAL_STREAM_PCM_OFFLOAD:
if (AudioDevice::sndCardState == CARD_STATUS_OFFLINE) {
*frames = astream_out->GetFramesWritten(timestamp);
astream_out->UpdatemCachedPosition(*frames);
break;
}
[[fallthrough]];
/* fall through if the card is online for PCM OFFLOAD stream */
case PAL_STREAM_COMPRESSED:
ret = astream_out->GetFrames(frames);
if (ret) {
AHAL_ERR("GetTimestamp failed %d", ret);
return ret;
}
clock_gettime(CLOCK_MONOTONIC, timestamp);
break;
default:
*frames = astream_out->GetFramesWritten(timestamp);
break;
}
} else {
//AHAL_ERR("unable to get audio stream");
return -EINVAL;
}
AHAL_VERBOSE("frames %lld played at %lld ", ((long long) *frames), timestamp->tv_sec * 1000000LL + timestamp->tv_nsec / 1000);
return ret;
}
static int out_get_render_position(const struct audio_stream_out *stream,
uint32_t *dsp_frames) {
std::ignore = stream;
std::ignore = dsp_frames;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
int ret = 0;
uint64_t frames = 0;
if (adevice) {
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
if (astream_out) {
switch (astream_out->GetPalStreamType(astream_out->flags_)) {
case PAL_STREAM_PCM_OFFLOAD:
if (AudioDevice::sndCardState == CARD_STATUS_OFFLINE) {
frames = astream_out->GetFramesWritten(NULL);
*dsp_frames = (uint32_t) frames;
astream_out->UpdatemCachedPosition(*dsp_frames);
break;
}
[[fallthrough]];
/* fall through if the card is online for PCM OFFLOAD stream */
case PAL_STREAM_COMPRESSED:
ret = astream_out->GetFrames(&frames);
if (ret) {
AHAL_ERR("Get DSP Frames failed %d", ret);
return ret;
}
*dsp_frames = (uint32_t) frames;
break;
case PAL_STREAM_LOW_LATENCY:
case PAL_STREAM_DEEP_BUFFER:
frames = astream_out->GetFramesWritten(NULL);
*dsp_frames = (uint32_t) frames;
break;
default:
break;
}
}
return 0;
}
static int astream_out_set_parameters(struct audio_stream *stream,
const char *kvpairs) {
int ret = 0;
struct str_parms *parms = (str_parms *)NULL;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (adevice) {
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
} else {
ret = -EINVAL;
AHAL_ERR("unable to get audio device");
return ret;
}
AHAL_DBG("enter: usecase(%d: %s) kvpairs: %s",
astream_out->GetUseCase(), use_case_table[astream_out->GetUseCase()], kvpairs);
parms = str_parms_create_str(kvpairs);
if (!parms) {
ret = -EINVAL;
goto exit;
}
ret = astream_out->SetParameters(parms);
if (ret) {
AHAL_ERR("Stream SetParameters Error (%x)", ret);
goto exit;
}
exit:
if (parms)
str_parms_destroy(parms);
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
static char* astream_out_get_parameters(const struct audio_stream *stream,
const char *keys) {
int ret = 0, hal_output_suspend_supported = 0;
struct str_parms *query = str_parms_create_str(keys);
char value[256];
char *str = (char*) nullptr;
std::shared_ptr<StreamOutPrimary> astream_out;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
struct str_parms *reply = str_parms_create();
AHAL_DBG("enter");
if (!query || !reply) {
if (reply)
str_parms_destroy(reply);
if (query)
str_parms_destroy(query);
AHAL_ERR("out_get_parameters: failed to allocate mem for query or reply");
return nullptr;
}
if (adevice) {
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
goto error;
}
if (!astream_out) {
AHAL_ERR("unable to get audio stream");
goto error;
}
AHAL_DBG("keys: %s", keys);
ret = str_parms_get_str(query, "is_direct_pcm_track", value, sizeof(value));
if (ret >= 0) {
value[0] = '\0';
if (astream_out->flags_ & AUDIO_OUTPUT_FLAG_DIRECT &&
!(astream_out->flags_ & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) {
AHAL_VERBOSE("in direct_pcm");
strlcat(value, "true", sizeof(value));
} else {
AHAL_VERBOSE("not in direct_pcm");
strlcat(value, "false", sizeof(value));
}
str_parms_add_str(reply, "is_direct_pcm_track", value);
if (str)
free(str);
str = str_parms_to_str(reply);
}
if (str_parms_get_str(query, "supports_hw_suspend", value, sizeof(value)) >= 0) {
//only low latency track supports suspend_resume
if (astream_out->flags_ == (AUDIO_OUTPUT_FLAG_FAST|AUDIO_OUTPUT_FLAG_PRIMARY))
hal_output_suspend_supported = 1;
str_parms_add_int(reply, "supports_hw_suspend", hal_output_suspend_supported);
if (str)
free(str);
str = str_parms_to_str(reply);
}
if (astream_out->GetSupportedConfig(true, query, reply))
str = str_parms_to_str(reply);
error:
str_parms_destroy(query);
str_parms_destroy(reply);
AHAL_DBG("exit: returns - %s", str);
return str;
}
static int astream_out_set_volume(struct audio_stream_out *stream,
float left, float right) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (adevice) {
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
if (astream_out) {
return astream_out->SetVolume(left, right);
} else {
AHAL_ERR("unable to get audio stream");
return -EINVAL;
}
}
static int astream_out_add_audio_effect(
const struct audio_stream *stream __unused,
effect_handle_t effect __unused) {
return 0;
}
static int astream_out_remove_audio_effect(
const struct audio_stream *stream __unused,
effect_handle_t effect __unused) {
return 0;
}
static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
size_t bytes) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
if (adevice) {
astream_in = adevice->InGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
if (astream_in) {
return astream_in->read(buffer, bytes);
} else {
AHAL_ERR("unable to get audio stream");
return -EINVAL;
}
return 0;
}
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
size_t bytes) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamOutPrimary> astream_out;
if (adevice) {
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
if (astream_out) {
return astream_out->write(buffer, bytes);
} else {
AHAL_ERR("unable to get audio stream");
return -EINVAL;
}
return 0;
}
static int astream_in_mmap_noirq_start(const struct audio_stream_in *stream)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_in = adevice->InGetStream((audio_stream_t*)stream);
if (!astream_in) {
AHAL_ERR("unable to get audio InStream");
return -EINVAL;
}
return astream_in->Start();
}
static int astream_in_mmap_noirq_stop(const struct audio_stream_in *stream)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_in = adevice->InGetStream((audio_stream_t*)stream);
if (!astream_in) {
AHAL_ERR("unable to get audio InStream");
return -EINVAL;
}
return astream_in->Stop();
}
static int astream_in_create_mmap_buffer(const struct audio_stream_in *stream,
int32_t min_size_frames, struct audio_mmap_buffer_info *info)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_in = adevice->InGetStream((audio_stream_t*)stream);
if (!astream_in) {
AHAL_ERR("unable to get audio InStream");
return -EINVAL;
}
if (info == NULL || !(min_size_frames > 0 && min_size_frames < INT32_MAX)) {
AHAL_ERR("invalid field info = %p, min_size_frames = %d", info, min_size_frames);
return -EINVAL;
}
if (astream_in->GetUseCase() != USECASE_AUDIO_RECORD_MMAP) {
AHAL_ERR("invalid usecase = %d", astream_in->GetUseCase());
return -ENOSYS;
}
return astream_in->CreateMmapBuffer(min_size_frames, info);
}
static int astream_in_get_mmap_position(const struct audio_stream_in *stream,
struct audio_mmap_position *position)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
if (!adevice) {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
astream_in = adevice->InGetStream((audio_stream_t*)stream);
if (!astream_in) {
AHAL_ERR("unable to get audio InStream");
return -EINVAL;
}
if (astream_in->GetUseCase() != USECASE_AUDIO_RECORD_MMAP) {
AHAL_ERR("usecase = %d", astream_in->GetUseCase());
return -ENOSYS;
}
return astream_in->GetMmapPosition(position);
}
static int astream_in_set_microphone_direction(
const struct audio_stream_in *stream,
audio_microphone_direction_t dir) {
std::ignore = stream;
std::ignore = dir;
AHAL_VERBOSE("function not implemented");
//No plans to implement audiozoom
return -ENOSYS;
}
static int in_set_microphone_field_dimension(
const struct audio_stream_in *stream,
float zoom) {
std::ignore = stream;
std::ignore = zoom;
AHAL_VERBOSE("function not implemented");
//No plans to implement audiozoom
return -ENOSYS;
}
static int astream_in_add_audio_effect(
const struct audio_stream *stream,
effect_handle_t effect)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
int ret = 0;
AHAL_DBG("Enter ");
if (adevice) {
astream_in = adevice->InGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
ret= -EINVAL;
goto exit;
}
if (astream_in) {
ret = astream_in->addRemoveAudioEffect(stream, effect, true);
} else {
AHAL_ERR("unable to get audio stream");
ret = -EINVAL;
goto exit;
}
exit:
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
static int astream_in_remove_audio_effect(const struct audio_stream *stream,
effect_handle_t effect)
{
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
int ret = 0;
AHAL_DBG("Enter ");
if (adevice) {
astream_in = adevice->InGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
ret = -EINVAL;
goto exit;
}
if (astream_in) {
ret = astream_in->addRemoveAudioEffect(stream, effect, false);
} else {
AHAL_ERR("unable to get audio stream");
ret = -EINVAL;
}
exit:
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int64_t StreamInPrimary::GetSourceLatency(audio_input_flags_t halStreamFlags)
{
// check how to get dsp_latency value from platform info xml instead of hardcoding
return 0;
/*struct pal_stream_attributes streamAttributes_;
streamAttributes_.type = StreamInPrimary::GetPalStreamType(halStreamFlags,
config_.sample_rate);
AHAL_VERBOSE(" type %d", streamAttributes_.type);
switch (streamAttributes_.type) {
case PAL_STREAM_DEEP_BUFFER:
return DEEP_BUFFER_PLATFORM_CAPTURE_DELAY;
case PAL_STREAM_LOW_LATENCY:
return LOW_LATENCY_PLATFORM_CAPTURE_DELAY;
case PAL_STREAM_VOIP_TX:
return VOIP_TX_PLATFORM_CAPTURE_DELAY;
case PAL_STREAM_RAW:
return RAW_STREAM_PLATFORM_CAPTURE_DELAY;
//TODO: Add more streamtypes if available in pal
default:
return 0;
}*/
}
uint64_t StreamInPrimary::GetFramesRead(int64_t* time)
{
uint64_t signed_frames = 0;
uint64_t kernel_frames = 0;
size_t kernel_buffer_size = 0;
int64_t dsp_latency = 0;
if (!time) {
AHAL_ERR("timestamp NULL");
return 0;
}
//TODO: need to get this latency from xml instead of hardcoding
stream_mutex_.lock();
dsp_latency = StreamInPrimary::GetSourceLatency(flags_);
if (usecase_ == USECASE_AUDIO_RECORD_COMPRESS) {
/**
* TODO get num of pcm frames from below layers
**/
signed_frames = mCompressReadCalls * 1024;
} else {
signed_frames = mBytesRead / audio_bytes_per_frame(
audio_channel_count_from_in_mask(config_.channel_mask),
config_.format);
}
*time = (readAt.tv_sec * 1000000000LL) + readAt.tv_nsec - (dsp_latency * 1000LL);
// Adjustment accounts for A2dp decoder latency
// Note: Decoder latency is returned in ms, while platform_source_latency in us.
pal_param_bta2dp_t* param_bt_a2dp = NULL;
size_t size = 0;
int32_t ret;
if (isDeviceAvailable(PAL_DEVICE_IN_BLUETOOTH_A2DP)) {
ret = pal_get_param(PAL_PARAM_ID_BT_A2DP_DECODER_LATENCY,
(void**)&param_bt_a2dp, &size, nullptr);
if (!ret && param_bt_a2dp) {
*time -= param_bt_a2dp->latency * 1000000LL;
}
}
stream_mutex_.unlock();
AHAL_VERBOSE("signed frames %lld", (long long)signed_frames);
return signed_frames;
}
static int astream_in_get_capture_position(const struct audio_stream_in* stream,
int64_t* frames, int64_t* time) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
if (stream == NULL || frames == NULL || time == NULL) {
return -EINVAL;
}
if (adevice) {
astream_in = adevice->InGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return -ENOSYS;
}
if(astream_in)
*frames = astream_in->GetFramesRead(time);
else
return -ENOSYS;
AHAL_VERBOSE("audio stream(%p) frames %lld played at %lld ",
astream_in.get(), ((long long)*frames), ((long long)*time));
return 0;
}
static uint32_t astream_in_get_input_frames_lost(
struct audio_stream_in *stream __unused) {
return 0;
}
static void in_update_sink_metadata_v7(
struct audio_stream_in *stream,
const struct sink_metadata_v7 *sink_metadata) {
if (stream == NULL
|| sink_metadata == NULL
|| sink_metadata->tracks == NULL) {
AHAL_ERR("%s: stream or sink_metadata is NULL", __func__);
return;
}
audio_devices_t device = sink_metadata->tracks->base.dest_device;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
int ret = 0;
AHAL_DBG("%s: sink device %d", __func__, device);
if (device == AUDIO_DEVICE_OUT_HEARING_AID) {
std::set<audio_devices_t> device_types;
device_types.insert(device);
if (adevice && adevice->voice_) {
ret = adevice->voice_->RouteStream(device_types);
AHAL_DBG("%s voice RouteStream ret = %d", __func__, ret);
}
else {
AHAL_ERR("%s: voice handle does not exist", __func__);
}
}
}
static int astream_in_get_active_microphones(
const struct audio_stream_in *stream,
struct audio_microphone_characteristic_t *mic_array,
size_t *mic_count) {
int noPalDevices = 0;
pal_device_id_t palDevs[MAX_ACTIVE_MICROPHONES_TO_SUPPORT];
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
uint32_t channels = 0;
uint32_t in_mic_count = 0;
uint32_t out_mic_count = 0;
uint32_t total_mic_count = 0;
int ret = 0;
if (mic_count == NULL) {
AHAL_ERR("Invalid mic_count!!!");
ret = -EINVAL;
goto done;
}
if (mic_array == NULL) {
AHAL_ERR("Invalid mic_array!!!");
ret = -EINVAL;
goto done;
}
if (*mic_count == 0) {
AHAL_INFO("mic_count is ZERO!!!");
goto done;
}
in_mic_count = (uint32_t)(*mic_count);
if (adevice) {
astream_in = adevice->InGetStream((audio_stream_t*)stream);
} else {
AHAL_INFO("unable to get audio device");
goto done;
}
if (astream_in) {
channels = astream_in->GetChannelMask();
memset(palDevs, 0, MAX_ACTIVE_MICROPHONES_TO_SUPPORT*sizeof(pal_device_id_t));
if (!(astream_in->GetPalDeviceIds(palDevs, &noPalDevices))) {
for (int i = 0; i < noPalDevices; i++) {
if (total_mic_count < in_mic_count) {
out_mic_count = in_mic_count - total_mic_count;
if (!adevice->get_active_microphones((uint32_t)channels, palDevs[i],
&mic_array[total_mic_count], &out_mic_count))
total_mic_count += out_mic_count;
}
}
}
}
done:
if (NULL != mic_count)
*mic_count = total_mic_count;
return ret;
}
static uint32_t astream_in_get_sample_rate(const struct audio_stream *stream) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
if (adevice) {
astream_in = adevice->InGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return 0;
}
if (astream_in)
return astream_in->GetSampleRate();
else
return 0;
}
static audio_channel_mask_t astream_in_get_channels(const struct audio_stream *stream) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
if (adevice) {
astream_in = adevice->InGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return (audio_channel_mask_t) 0;
}
if (astream_in) {
return astream_in->GetChannelMask();
} else {
AHAL_ERR("unable to get audio stream");
return (audio_channel_mask_t) 0;
}
}
static audio_format_t astream_in_get_format(const struct audio_stream *stream) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
if (adevice)
astream_in = adevice->InGetStream((audio_stream_t*)stream);
else
AHAL_ERR("unable to get audio device");
if (astream_in)
return astream_in->GetFormat();
else
return AUDIO_FORMAT_DEFAULT;
}
static int astream_in_standby(struct audio_stream *stream) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
int ret = 0;
if (adevice) {
astream_in = adevice->InGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
ret = -EINVAL;
return ret;
}
AHAL_DBG("enter: stream (%p) usecase(%d: %s)", astream_in.get(),
astream_in->GetUseCase(), use_case_table[astream_in->GetUseCase()]);
if (astream_in) {
ret = astream_in->Standby();
} else {
AHAL_ERR("unable to get audio stream");
ret = -EINVAL;
}
exit:
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
static int astream_in_set_parameters(struct audio_stream *stream, const char *kvpairs) {
int ret = 0;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
AHAL_DBG("Enter: %s",kvpairs);
if (!stream || !kvpairs) {
ret = 0;
goto error;
}
if (adevice) {
astream_in = adevice->InGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
if (astream_in) {
return astream_in->SetParameters(kvpairs);
}
error:
AHAL_DBG("Exit: %d",ret);
return ret;
}
static char* astream_in_get_parameters(const struct audio_stream *stream,
const char *keys) {
std::ignore = stream;
std::ignore = keys;
struct str_parms *query = str_parms_create_str(keys);
char value[256];
char *str = (char*) nullptr;
std::shared_ptr<StreamInPrimary> astream_in;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
struct str_parms *reply = str_parms_create();
int ret = 0;
AHAL_DBG("enter");
if (!query || !reply) {
if (reply)
str_parms_destroy(reply);
if (query)
str_parms_destroy(query);
AHAL_ERR("in_get_parameters: failed to allocate mem for query or reply");
return nullptr;
}
if (adevice) {
astream_in = adevice->InGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
goto error;
}
if (!astream_in) {
AHAL_ERR("unable to get audio stream");
goto error;
}
AHAL_DBG("keys: %s", keys);
astream_in->GetSupportedConfig(false, query, reply);
astream_in->getParameters(query,reply);
str = str_parms_to_str(reply);
error:
str_parms_destroy(query);
str_parms_destroy(reply);
AHAL_DBG("exit: returns - %s", str);
return str;
}
static int astream_in_set_gain(struct audio_stream_in *stream, float gain) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in;
if (adevice) {
astream_in = adevice->InGetStream((audio_stream_t*)stream);
} else {
AHAL_ERR("unable to get audio device");
return -EINVAL;
}
if (astream_in) {
return astream_in->SetGain(gain);
} else {
AHAL_ERR("unable to get audio stream");
return -EINVAL;
}
}
static size_t astream_in_get_buffer_size(const struct audio_stream *stream) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
std::shared_ptr<StreamInPrimary> astream_in =
adevice->InGetStream((audio_stream_t*)stream);
if (astream_in)
return astream_in->GetBufferSize();
else
return 0;
}
int StreamPrimary::getPalDeviceIds(const std::set<audio_devices_t>& halDeviceIds,
pal_device_id_t* qualIds) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
return adevice->GetPalDeviceIds(halDeviceIds, qualIds);
}
int StreamPrimary::GetDeviceAddress(struct str_parms *parms, int *card_id,
int *device_num) {
int ret = -EINVAL;
char value[64];
ret = str_parms_get_str(parms, "card", value, sizeof(value));
if (ret >= 0) {
*card_id = atoi(value);
ret = str_parms_get_str(parms, "device", value, sizeof(value));
if (ret >= 0) {
*device_num = atoi(value);
}
}
return ret;
}
int StreamPrimary::GetLookupTableIndex(const struct string_to_enum *table,
int table_size, int value) {
int index = -EINVAL;
int i = 0;
for (i = 0; i < table_size; i++) {
if (value == table[i].value) {
index = i;
break;
}
}
return index;
}
pal_stream_type_t StreamInPrimary::GetPalStreamType(
audio_input_flags_t halStreamFlags,
uint32_t sample_rate) {
pal_stream_type_t palStreamType = PAL_STREAM_LOW_LATENCY;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
if ((halStreamFlags & AUDIO_INPUT_FLAG_VOIP_TX)!=0) {
palStreamType = PAL_STREAM_VOIP_TX;
return palStreamType;
}
if (sample_rate == LOW_LATENCY_CAPTURE_SAMPLE_RATE &&
(halStreamFlags & AUDIO_INPUT_FLAG_TIMESTAMP) == 0 &&
(halStreamFlags & AUDIO_INPUT_FLAG_COMPRESS) == 0 &&
(halStreamFlags & AUDIO_INPUT_FLAG_FAST) != 0) {
if (isDeviceAvailable(PAL_DEVICE_IN_PROXY))
palStreamType = PAL_STREAM_PROXY;
else
palStreamType = PAL_STREAM_ULTRA_LOW_LATENCY;
return palStreamType;
}
/*
* check for input direct flag which is exclusive
* meant for compress offload capture.
*/
if ((halStreamFlags & AUDIO_INPUT_FLAG_DIRECT) != 0) {
palStreamType = PAL_STREAM_COMPRESSED;
return palStreamType;
}
/*
*For AUDIO_SOURCE_UNPROCESSED we use LL pal stream as it corresponds to
*RAW record graphs ( record with no pp)
*/
if (source_ == AUDIO_SOURCE_UNPROCESSED) {
palStreamType = PAL_STREAM_RAW;
return palStreamType;
} else if (source_ == AUDIO_SOURCE_VOICE_RECOGNITION) {
palStreamType = PAL_STREAM_VOICE_RECOGNITION;
if (halStreamFlags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) {
palStreamType = PAL_STREAM_ULTRA_LOW_LATENCY;
}
return palStreamType;
}
switch (halStreamFlags) {
case AUDIO_INPUT_FLAG_FAST:
palStreamType = PAL_STREAM_LOW_LATENCY;
break;
case AUDIO_INPUT_FLAG_RAW:
palStreamType = PAL_STREAM_RAW;
break;
case AUDIO_INPUT_FLAG_VOIP_TX:
palStreamType = PAL_STREAM_VOIP_TX;
break;
case AUDIO_INPUT_FLAG_MMAP_NOIRQ:
palStreamType = PAL_STREAM_ULTRA_LOW_LATENCY;
break;
case AUDIO_INPUT_FLAG_HW_HOTWORD:
case AUDIO_INPUT_FLAG_NONE:
palStreamType = PAL_STREAM_DEEP_BUFFER;
if (source_ == AUDIO_SOURCE_VOICE_UPLINK ||
source_ == AUDIO_SOURCE_VOICE_DOWNLINK ||
source_ == AUDIO_SOURCE_VOICE_CALL) {
if (isDeviceAvailable(PAL_DEVICE_IN_TELEPHONY_RX) ||
(adevice && adevice->voice_ && adevice->voice_->IsAnyCallActive())) {
palStreamType = PAL_STREAM_VOICE_CALL_RECORD;
}
#ifdef EC_REF_CAPTURE_ENABLED
} else if (source_ == AUDIO_SOURCE_ECHO_REFERENCE) {
palStreamType = PAL_STREAM_RAW;
#endif
} else {
if (isDeviceAvailable(PAL_DEVICE_IN_TELEPHONY_RX)) {
palStreamType = PAL_STREAM_PROXY;
}
}
break;
default:
/*
unsupported from PAL
AUDIO_INPUT_FLAG_SYNC = 0x8,
AUDIO_INPUT_FLAG_HW_AV_SYNC = 0x40,
*/
AHAL_ERR("error flag %#x is not supported from PAL." ,
halStreamFlags);
break;
}
return palStreamType;
}
pal_stream_type_t StreamOutPrimary::GetPalStreamType(
audio_output_flags_t halStreamFlags) {
pal_stream_type_t palStreamType = PAL_STREAM_LOW_LATENCY;
if ((halStreamFlags & AUDIO_OUTPUT_FLAG_VOIP_RX)!=0) {
palStreamType = PAL_STREAM_VOIP_RX;
return palStreamType;
}
if ((halStreamFlags & AUDIO_OUTPUT_FLAG_RAW) != 0) {
palStreamType = PAL_STREAM_ULTRA_LOW_LATENCY;
} else if ((halStreamFlags & AUDIO_OUTPUT_FLAG_FAST) != 0) {
palStreamType = PAL_STREAM_LOW_LATENCY;
} else if (halStreamFlags ==
(AUDIO_OUTPUT_FLAG_FAST|AUDIO_OUTPUT_FLAG_RAW)) {
palStreamType = PAL_STREAM_RAW;
} else if (halStreamFlags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
palStreamType = PAL_STREAM_DEEP_BUFFER;
} else if (halStreamFlags ==
(AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ)) {
// mmap_no_irq_out: to be confirmed
palStreamType = PAL_STREAM_ULTRA_LOW_LATENCY;
} else if (halStreamFlags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
palStreamType = PAL_STREAM_ULTRA_LOW_LATENCY;
} else if (halStreamFlags & AUDIO_OUTPUT_FLAG_RAW) {
palStreamType = PAL_STREAM_ULTRA_LOW_LATENCY;
} else if (halStreamFlags == (AUDIO_OUTPUT_FLAG_DIRECT|
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|
AUDIO_OUTPUT_FLAG_NON_BLOCKING)) {
// hifi: to be confirmed
palStreamType = PAL_STREAM_COMPRESSED;
} else if (halStreamFlags == AUDIO_OUTPUT_FLAG_DIRECT) {
palStreamType = PAL_STREAM_PCM_OFFLOAD;
} else if (halStreamFlags == (AUDIO_OUTPUT_FLAG_DIRECT|
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|
AUDIO_OUTPUT_FLAG_NON_BLOCKING)) {
palStreamType = PAL_STREAM_COMPRESSED;
} else if (halStreamFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
// dsd_compress_passthrough
palStreamType = PAL_STREAM_COMPRESSED;
} else if (halStreamFlags == (AUDIO_OUTPUT_FLAG_DIRECT|
AUDIO_OUTPUT_FLAG_VOIP_RX)) {
// voice rx
palStreamType = PAL_STREAM_VOIP_RX;
} else if (halStreamFlags == AUDIO_OUTPUT_FLAG_VOIP_RX) {
palStreamType = PAL_STREAM_VOIP_RX;
} else if (halStreamFlags == AUDIO_OUTPUT_FLAG_INCALL_MUSIC) {
// incall_music_uplink
palStreamType = PAL_STREAM_VOICE_CALL_MUSIC;
} else {
palStreamType = PAL_STREAM_GENERIC;
}
return palStreamType;
}
int StreamOutPrimary::FillHalFnPtrs() {
int ret = 0;
stream_.get()->common.get_sample_rate = astream_out_get_sample_rate;
stream_.get()->common.set_sample_rate = astream_set_sample_rate;
stream_.get()->common.get_buffer_size = astream_out_get_buffer_size;
stream_.get()->common.get_channels = astream_out_get_channels;
stream_.get()->common.get_format = astream_out_get_format;
stream_.get()->common.set_format = astream_set_format;
stream_.get()->common.standby = astream_out_standby;
stream_.get()->common.dump = astream_dump;
stream_.get()->common.set_parameters = astream_out_set_parameters;
stream_.get()->common.get_parameters = astream_out_get_parameters;
stream_.get()->common.add_audio_effect = astream_out_add_audio_effect;
stream_.get()->common.remove_audio_effect =
astream_out_remove_audio_effect;
stream_.get()->get_latency = astream_get_latency;
stream_.get()->set_volume = astream_out_set_volume;
stream_.get()->write = out_write;
stream_.get()->get_render_position = out_get_render_position;
stream_.get()->get_next_write_timestamp =
astream_out_get_next_write_timestamp;
stream_.get()->get_presentation_position =
astream_out_get_presentation_position;
stream_.get()->update_source_metadata = NULL;
stream_.get()->pause = astream_pause;
stream_.get()->resume = astream_resume;
stream_.get()->drain = astream_drain;
stream_.get()->flush = astream_flush;
stream_.get()->set_callback = astream_set_callback;
return ret;
}
int StreamOutPrimary::GetMmapPosition(struct audio_mmap_position *position)
{
struct pal_mmap_position pal_mmap_pos;
int32_t ret = 0;
stream_mutex_.lock();
if (pal_stream_handle_ == nullptr) {
AHAL_ERR("error pal handle is null\n");
stream_mutex_.unlock();
return -EINVAL;
}
ret = pal_stream_get_mmap_position(pal_stream_handle_, &pal_mmap_pos);
if (ret) {
AHAL_ERR("failed to get mmap position %d\n", ret);
stream_mutex_.unlock();
return ret;
}
position->position_frames = pal_mmap_pos.position_frames;
position->time_nanoseconds = pal_mmap_pos.time_nanoseconds;
#if 0
/** Check if persist vendor property is available */
const int32_t kDefaultOffsetMicros = 0;
int32_t mmap_time_offset_micros = property_get_int32(
"persist.vendor.audio.out_mmap_delay_micros", kDefaultOffsetMicros);
position->time_nanoseconds += mmap_time_offset_micros * (int64_t)1000;
#endif
stream_mutex_.unlock();
return 0;
}
bool StreamOutPrimary::isDeviceAvailable(pal_device_id_t deviceId)
{
for (int i = 0; i < mAndroidOutDevices.size(); i++) {
if (mPalOutDevice[i].id == deviceId)
return true;
}
return false;
}
int StreamOutPrimary::CreateMmapBuffer(int32_t min_size_frames,
struct audio_mmap_buffer_info *info)
{
int ret;
struct pal_mmap_buffer palMmapBuf;
stream_mutex_.lock();
if (pal_stream_handle_) {
AHAL_ERR("error pal handle already created\n");
stream_mutex_.unlock();
return -EINVAL;
}
ret = Open();
if (ret) {
AHAL_ERR("failed to open stream.");
stream_mutex_.unlock();
return ret;
}
ret = pal_stream_create_mmap_buffer(pal_stream_handle_,
min_size_frames, &palMmapBuf);
if (ret) {
AHAL_ERR("failed to create mmap buffer: %d", ret);
// release stream lock as Standby will lock/unlock stream mutex
stream_mutex_.unlock();
Standby();
return ret;
}
info->shared_memory_address = palMmapBuf.buffer;
info->shared_memory_fd = palMmapBuf.fd;
info->buffer_size_frames = palMmapBuf.buffer_size_frames;
info->burst_size_frames = palMmapBuf.burst_size_frames;
info->flags = (audio_mmap_buffer_flag) AUDIO_MMAP_APPLICATION_SHAREABLE;
mmap_shared_memory_fd = info->shared_memory_fd;
stream_mutex_.unlock();
return ret;
}
int StreamOutPrimary::Stop() {
int ret = -ENOSYS;
AHAL_INFO("Enter: OutPrimary usecase(%d: %s)", GetUseCase(), use_case_table[GetUseCase()]);
stream_mutex_.lock();
if (usecase_ == USECASE_AUDIO_PLAYBACK_MMAP &&
pal_stream_handle_ && stream_started_) {
ret = pal_stream_stop(pal_stream_handle_);
if (ret == 0) {
stream_started_ = false;
stream_paused_ = false;
}
}
stream_mutex_.unlock();
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int StreamOutPrimary::Start() {
int ret = -ENOSYS;
AHAL_INFO("Enter: OutPrimary usecase(%d: %s)", GetUseCase(), use_case_table[GetUseCase()]);
stream_mutex_.lock();
if (usecase_ == USECASE_AUDIO_PLAYBACK_MMAP &&
pal_stream_handle_ && !stream_started_) {
ret = pal_stream_start(pal_stream_handle_);
if (ret == 0)
stream_started_ = true;
}
if (karaoke)
AudExtn.karaoke_start();
stream_mutex_.unlock();
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int StreamOutPrimary::Pause() {
int ret = 0;
AHAL_INFO("Enter: usecase(%d: %s)", GetUseCase(), use_case_table[GetUseCase()]);
stream_mutex_.lock();
if (!pal_stream_handle_ || !stream_started_) {
AHAL_DBG("Stream not started yet");
ret = -1;
goto exit;
}
// only direct stream will receive pause/resume cmd from AudioFlinger,
// VOIP RX is specified to direct output in qcom audio policy config,
// which doesn't need pause/resume actually.
if (streamAttributes_.type == PAL_STREAM_VOIP_RX) {
AHAL_DBG("no need to pause for VOIP RX: %d");
ret = -1;
goto exit;
}
if (pal_stream_handle_) {
ret = pal_stream_pause(pal_stream_handle_);
}
if (ret)
ret = -EINVAL;
else {
stream_paused_ = true;
}
exit:
stream_mutex_.unlock();
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int StreamOutPrimary::Resume() {
int ret = 0;
AHAL_INFO("Enter: usecase(%d: %s)", GetUseCase(), use_case_table[GetUseCase()]);
stream_mutex_.lock();
if (!pal_stream_handle_ || !stream_started_) {
AHAL_DBG("Stream not started yet");
ret = -1;
goto exit;
}
if (pal_stream_handle_) {
ret = pal_stream_resume(pal_stream_handle_);
}
if (ret)
ret = -EINVAL;
else {
stream_paused_ = false;
}
exit:
stream_mutex_.unlock();
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int StreamOutPrimary::Flush() {
int ret = 0;
AHAL_INFO("Enter: usecase(%d: %s)", GetUseCase(), use_case_table[GetUseCase()]);
stream_mutex_.lock();
if (pal_stream_handle_) {
if(stream_paused_ == true)
{
ret = pal_stream_flush(pal_stream_handle_);
if (!ret) {
ret = pal_stream_resume(pal_stream_handle_);
if (!ret)
stream_paused_ = false;
}
} else {
AHAL_INFO("called in invalid state (stream not paused)" );
}
mBytesWritten = 0;
}
sendGaplessMetadata = true;
stream_mutex_.unlock();
if (ret)
ret = -EINVAL;
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int StreamOutPrimary::Drain(audio_drain_type_t type) {
int ret = 0;
pal_drain_type_t palDrainType;
AHAL_INFO("Enter: usecase(%d: %s)", GetUseCase(), use_case_table[GetUseCase()]);
switch (type) {
case AUDIO_DRAIN_ALL:
palDrainType = PAL_DRAIN;
break;
case AUDIO_DRAIN_EARLY_NOTIFY:
palDrainType = PAL_DRAIN_PARTIAL;
break;
default:
AHAL_ERR("Invalid drain type:%d", type);
return -EINVAL;
}
stream_mutex_.lock();
if (pal_stream_handle_)
ret = pal_stream_drain(pal_stream_handle_, palDrainType);
stream_mutex_.unlock();
if (ret) {
AHAL_ERR("Invalid drain type:%d", type);
}
return ret;
}
void StreamOutPrimary::UpdatemCachedPosition(uint64_t val)
{
mCachedPosition = val;
}
int StreamOutPrimary::Standby() {
int ret = 0;
AHAL_DBG("Enter");
stream_mutex_.lock();
if (pal_stream_handle_) {
if (streamAttributes_.type == PAL_STREAM_PCM_OFFLOAD) {
/*
* when ssr happens, dsp position for pcm offload could be 0,
* so get written frames. Else, get frames.
*/
if (AudioDevice::sndCardState == CARD_STATUS_OFFLINE) {
struct timespec ts;
// release stream lock as GetFramesWritten will lock/unlock stream mutex
stream_mutex_.unlock();
mCachedPosition = GetFramesWritten(&ts);
stream_mutex_.lock();
AHAL_DBG("card is offline, return written frames %lld", (long long)mCachedPosition);
} else {
GetFrames(&mCachedPosition);
}
}
ret = pal_stream_stop(pal_stream_handle_);
if (ret) {
AHAL_ERR("failed to stop stream.");
ret = -EINVAL;
}
if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS && pal_haptics_stream_handle) {
ret = pal_stream_stop(pal_haptics_stream_handle);
if (ret) {
AHAL_ERR("failed to stop haptics stream.");
}
}
}
stream_started_ = false;
stream_paused_ = false;
sendGaplessMetadata = true;
if (CheckOffloadEffectsType(streamAttributes_.type)) {
ret = StopOffloadEffects(handle_, pal_stream_handle_);
ret = StopOffloadVisualizer(handle_, pal_stream_handle_);
}
if (pal_stream_handle_) {
ret = pal_stream_close(pal_stream_handle_);
pal_stream_handle_ = NULL;
if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS && pal_haptics_stream_handle) {
ret = pal_stream_close(pal_haptics_stream_handle);
pal_haptics_stream_handle = NULL;
if (hapticBuffer) {
free (hapticBuffer);
hapticBuffer = NULL;
}
hapticsBufSize = 0;
if (hapticsDevice) {
free(hapticsDevice);
hapticsDevice = NULL;
}
}
}
if (karaoke) {
ret = AudExtn.karaoke_stop();
if (ret) {
AHAL_ERR("failed to stop karaoke path.");
ret = 0;
} else {
ret = AudExtn.karaoke_close();
if (ret) {
AHAL_ERR("failed to close karaoke path.");
ret = 0;
}
}
}
if (mmap_shared_memory_fd >= 0) {
close(mmap_shared_memory_fd);
mmap_shared_memory_fd = -1;
}
if (ret)
ret = -EINVAL;
exit:
stream_mutex_.unlock();
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int StreamOutPrimary::RouteStream(const std::set<audio_devices_t>& new_devices, bool force_device_switch __unused) {
int ret = 0, noPalDevices = 0;
pal_device_id_t * deviceId = nullptr;
struct pal_device* deviceIdConfigs = nullptr;
pal_param_device_capability_t *device_cap_query = nullptr;
size_t payload_size = 0;
dynamic_media_config_t dynamic_media_config;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
bool isHifiFilterEnabled = false;
bool *payload_hifiFilter = &isHifiFilterEnabled;
size_t param_size = 0;
stream_mutex_.lock();
if (!mInitialized) {
AHAL_ERR("Not initialized, returning error");
ret = -EINVAL;
goto done;
}
AHAL_INFO("enter: usecase(%d: %s) devices 0x%x, num devices %zu",
this->GetUseCase(), use_case_table[this->GetUseCase()],
AudioExtn::get_device_types(new_devices), new_devices.size());
AHAL_DBG("mAndroidOutDevices %d, mNoOfOutDevices %zu",
AudioExtn::get_device_types(mAndroidOutDevices),
mAndroidOutDevices.size());
if (!AudioExtn::audio_devices_empty(new_devices)) {
// re-allocate mPalOutDevice and mPalOutDeviceIds
if (new_devices.size() != mAndroidOutDevices.size()) {
deviceId = (pal_device_id_t*) realloc(mPalOutDeviceIds,
new_devices.size() * sizeof(pal_device_id_t));
deviceIdConfigs = (struct pal_device*) realloc(mPalOutDevice,
new_devices.size() * sizeof(struct pal_device));
if (!deviceId || !deviceIdConfigs) {
AHAL_ERR("Failed to allocate PalOutDeviceIds or deviceIdConfigs!");
if (deviceId)
mPalOutDeviceIds = deviceId;
if (deviceIdConfigs)
mPalOutDevice = deviceIdConfigs;
ret = -ENOMEM;
goto done;
}
// init deviceId and deviceIdConfigs
memset(deviceId, 0, new_devices.size() * sizeof(pal_device_id_t));
memset(deviceIdConfigs, 0, new_devices.size() * sizeof(struct pal_device));
mPalOutDeviceIds = deviceId;
mPalOutDevice = deviceIdConfigs;
}
noPalDevices = getPalDeviceIds(new_devices, mPalOutDeviceIds);
AHAL_DBG("noPalDevices: %d , new_devices: %zu",
noPalDevices, new_devices.size());
if (noPalDevices != new_devices.size() ||
noPalDevices >= PAL_DEVICE_IN_MAX) {
AHAL_ERR("Device count mismatch! Expected: %zu Got: %d",
new_devices.size(), noPalDevices);
ret = -EINVAL;
goto done;
}
device_cap_query = (pal_param_device_capability_t *)
malloc(sizeof(pal_param_device_capability_t));
if (!device_cap_query) {
AHAL_ERR("Failed to allocate device_cap_query!");
ret = -ENOMEM;
goto done;
}
ret = pal_get_param(PAL_PARAM_ID_HIFI_PCM_FILTER,
(void **)&payload_hifiFilter, &param_size, nullptr);
for (int i = 0; i < noPalDevices; i++) {
mPalOutDevice[i].id = mPalOutDeviceIds[i];
mPalOutDevice[i].config.sample_rate = mPalOutDevice[0].config.sample_rate;
mPalOutDevice[i].config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
mPalOutDevice[i].config.ch_info = {0, {0}};
mPalOutDevice[i].config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE;
if (((mPalOutDeviceIds[i] == PAL_DEVICE_OUT_USB_DEVICE) ||
(mPalOutDeviceIds[i] == PAL_DEVICE_OUT_USB_HEADSET)) && device_cap_query) {
mPalOutDevice[i].address.card_id = adevice->usb_card_id_;
mPalOutDevice[i].address.device_num = adevice->usb_dev_num_;
device_cap_query->id = mPalOutDeviceIds[i];
device_cap_query->addr.card_id = adevice->usb_card_id_;
device_cap_query->addr.device_num = adevice->usb_dev_num_;
device_cap_query->config = &dynamic_media_config;
device_cap_query->is_playback = true;
ret = pal_get_param(PAL_PARAM_ID_DEVICE_CAPABILITY,(void **)&device_cap_query,
&payload_size, nullptr);
if (ret<0){
AHAL_ERR("Error usb device is not connected");
ret = -ENOSYS;
goto done;
}
}
strlcpy(mPalOutDevice[i].custom_config.custom_key, "",
sizeof(mPalOutDevice[i].custom_config.custom_key));
if ((AudioExtn::audio_devices_cmp(mAndroidOutDevices, AUDIO_DEVICE_OUT_SPEAKER_SAFE)) &&
(mPalOutDeviceIds[i] == PAL_DEVICE_OUT_SPEAKER)) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "speaker-safe;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
AHAL_INFO("Setting custom key as %s", mPalOutDevice[i].custom_config.custom_key);
}
if (!ret && isHifiFilterEnabled &&
(mPalOutDevice[i].id == PAL_DEVICE_OUT_WIRED_HEADSET ||
mPalOutDevice[i].id == PAL_DEVICE_OUT_WIRED_HEADPHONE) &&
(config_.sample_rate != 384000 && config_.sample_rate != 352800)) {
AHAL_DBG("hifi-filter custom key sent to PAL (only applicable to certain streams)\n");
strlcat(mPalOutDevice[i].custom_config.custom_key,
"hifi-filter_custom_key;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
}
#ifdef DYNAMIC_SR_ENABLED
if (((usecase_ == USECASE_AUDIO_PLAYBACK_VOIP) ||
(usecase_ == USECASE_AUDIO_PLAYBACK_DEEP_BUFFER) ||
(isOffloadUsecase())) &&
((mPalOutDevice[i].id == PAL_DEVICE_OUT_SPEAKER) ||
(mPalOutDevice[i].id == PAL_DEVICE_OUT_HANDSET) ||
(mPalOutDevice[i].id == PAL_DEVICE_OUT_WIRED_HEADPHONE) ||
(mPalOutDevice[i].id == PAL_DEVICE_OUT_WIRED_HEADSET) ||
(mPalOutDevice[i].id == PAL_DEVICE_OUT_USB_DEVICE) ||
(mPalOutDevice[i].id == PAL_DEVICE_OUT_USB_HEADSET))) {
if (config_.sample_rate == 8000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "8K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 11025) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "11K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 16000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "16K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 22050) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "22K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 24000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "24K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 32000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "32K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 44100) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "44.1K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 48000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "48K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 64000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "64K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 88200) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "88.2K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 96000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "96K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 176400) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "176.4K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 192000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "192K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 352800) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "352.8K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 384000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "384K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else {
AHAL_DBG("No custom config to set for usecase %d for sr %d",
usecase_, config_.sample_rate);
}
AHAL_DBG("setting SR for usecase %d as %d", usecase_, config_.sample_rate);
}
#endif
}
mAndroidOutDevices = new_devices;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
if (adevice->hac_voip && (mPalOutDevice->id == PAL_DEVICE_OUT_HANDSET)) {
strlcat(mPalOutDevice->custom_config.custom_key, "HAC;",
sizeof(mPalOutDevice->custom_config.custom_key));
}
if (pal_stream_handle_) {
ret = pal_stream_set_device(pal_stream_handle_, noPalDevices, mPalOutDevice);
if (!ret) {
for (const auto &dev : mAndroidOutDevices)
audio_extn_gef_notify_device_config(dev,
config_.channel_mask,
config_.sample_rate, flags_);
} else {
AHAL_ERR("failed to set device. Error %d" ,ret);
}
}
}
done:
if (device_cap_query) {
free(device_cap_query);
device_cap_query = NULL;
}
stream_mutex_.unlock();
AHAL_DBG("exit %d", ret);
return ret;
}
int StreamOutPrimary::SetParameters(struct str_parms *parms) {
char value[64];
int ret = 0, controller = -1, stream = -1;
int ret1 = 0;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
AHAL_DBG("enter ");
if (!mInitialized)
goto error;
ret = AudioExtn::get_controller_stream_from_params(parms, &controller, &stream);
if (ret >= 0) {
adevice->dp_controller = controller;
adevice->dp_stream = stream;
if (stream >= 0 || controller >= 0)
AHAL_INFO("ret %d, plugin device cont %d stream %d", ret, controller, stream);
} else {
AHAL_ERR("error %d, failed to get stream and controller", ret);
}
//Parse below metadata only if it is compress offload usecase.
if (usecase_ == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
ret = AudioExtn::audio_extn_parse_compress_metadata(&config_, &palSndDec, parms,
&msample_rate, &mchannels, &isCompressMetadataAvail);
if (ret) {
AHAL_ERR("parse_compress_metadata Error (%x)", ret);
goto error;
}
ret1 = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES, value, sizeof(value));
if (ret1 >= 0 ) {
gaplessMeta.encoderDelay = atoi(value);
AHAL_DBG("new encoder delay %u", gaplessMeta.encoderDelay);
} else {
gaplessMeta.encoderDelay = 0;
}
ret1 = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES, value, sizeof(value));
if (ret1 >= 0) {
gaplessMeta.encoderPadding = atoi(value);
AHAL_DBG("padding %u", gaplessMeta.encoderPadding);
} else {
gaplessMeta.encoderPadding = 0;
}
}
error:
AHAL_DBG("exit %d", ret);
return ret;
}
int StreamOutPrimary::SetVolume(float left , float right) {
int ret = 0;
AHAL_DBG("Enter: left %f, right %f for usecase(%d: %s)", left, right, GetUseCase(), use_case_table[GetUseCase()]);
stream_mutex_.lock();
/* free previously cached volume if any */
if (volume_) {
free(volume_);
volume_ = NULL;
}
if (audio_channel_count_from_out_mask(config_.channel_mask) == 1) {
volume_ = (struct pal_volume_data *)calloc(1, sizeof(struct pal_volume_data)
+sizeof(struct pal_channel_vol_kv));
if (!volume_) {
AHAL_ERR("Failed to allocate mem for volume_");
ret = -ENOMEM;
goto done;
}
volume_->no_of_volpair = 1;
volume_->volume_pair[0].channel_mask = 0x03;
if (config_.channel_mask == 0x1)
volume_->volume_pair[0].vol = left;
else if (config_.channel_mask == 0x2)
volume_->volume_pair[0].vol = right;
else
volume_->volume_pair[0].vol = (left + right)/2.0;
} else {
volume_ = (struct pal_volume_data *)calloc(1, sizeof(struct pal_volume_data)
+sizeof(struct pal_channel_vol_kv) * 2);
if (!volume_) {
AHAL_ERR("Failed to allocate mem for volume_");
ret = -ENOMEM;
goto done;
}
volume_->no_of_volpair = 2;
volume_->volume_pair[0].channel_mask = 0x01;
volume_->volume_pair[0].vol = left;
volume_->volume_pair[1].channel_mask = 0x02;
volume_->volume_pair[1].vol = right;
}
/* if stream is not opened already cache the volume and set on open */
if (pal_stream_handle_) {
ret = pal_stream_set_volume(pal_stream_handle_, volume_);
if (ret) {
AHAL_ERR("Pal Stream volume Error (%x)", ret);
}
}
done:
stream_mutex_.unlock();
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
/* Delay in Us */
/* Delay in Us, only to be used for PCM formats */
int64_t StreamOutPrimary::GetRenderLatency(audio_output_flags_t halStreamFlags)
{
struct pal_stream_attributes streamAttributes_;
streamAttributes_.type = StreamOutPrimary::GetPalStreamType(halStreamFlags);
AHAL_VERBOSE(" type %d",streamAttributes_.type);
switch (streamAttributes_.type) {
case PAL_STREAM_DEEP_BUFFER:
return DEEP_BUFFER_PLATFORM_DELAY;
case PAL_STREAM_LOW_LATENCY:
return LOW_LATENCY_PLATFORM_DELAY;
case PAL_STREAM_COMPRESSED:
case PAL_STREAM_PCM_OFFLOAD:
return PCM_OFFLOAD_PLATFORM_DELAY;
case PAL_STREAM_ULTRA_LOW_LATENCY:
return ULL_PLATFORM_DELAY;
//TODO: Add more usecases/type as in current hal, once they are available in pal
default:
return 0;
}
}
uint64_t StreamOutPrimary::GetFramesWritten(struct timespec *timestamp)
{
uint64_t signed_frames = 0;
uint64_t written_frames = 0;
uint64_t kernel_frames = 0;
uint64_t dsp_frames = 0;
uint64_t bt_extra_frames = 0;
pal_param_bta2dp_t *param_bt_a2dp = NULL;
size_t size = 0, kernel_buffer_size = 0;
int32_t ret;
stream_mutex_.lock();
/* This adjustment accounts for buffering after app processor
* It is based on estimated DSP latency per use case, rather than exact.
*/
dsp_frames = StreamOutPrimary::GetRenderLatency(flags_) *
(streamAttributes_.out_media_config.sample_rate) / 1000000LL;
written_frames = mBytesWritten / audio_bytes_per_frame(
audio_channel_count_from_out_mask(config_.channel_mask),
config_.format);
/* not querying actual state of buffering in kernel as it would involve an ioctl call
* which then needs protection, this causes delay in TS query for pcm_offload usecase
* hence only estimate.
*/
kernel_buffer_size = fragment_size_ * fragments_;
kernel_frames = kernel_buffer_size /
audio_bytes_per_frame(
audio_channel_count_from_out_mask(config_.channel_mask),
config_.format);
// kernel_frames = (kernel_buffer_size - avail) / (bitwidth * channel count);
if (written_frames >= (kernel_frames + dsp_frames))
signed_frames = written_frames - (kernel_frames + dsp_frames);
// Adjustment accounts for A2dp encoder latency with non offload usecases
// Note: Encoder latency is returned in ms, while platform_render_latency in us.
if (isDeviceAvailable(PAL_DEVICE_OUT_BLUETOOTH_A2DP)) {
ret = pal_get_param(PAL_PARAM_ID_BT_A2DP_ENCODER_LATENCY,
(void **)&param_bt_a2dp, &size, nullptr);
if (!ret && param_bt_a2dp) {
bt_extra_frames = param_bt_a2dp->latency *
(streamAttributes_.out_media_config.sample_rate) / 1000;
if (signed_frames >= bt_extra_frames)
signed_frames -= bt_extra_frames;
}
}
stream_mutex_.unlock();
if (signed_frames <= 0) {
signed_frames = 0;
if (timestamp != NULL)
clock_gettime(CLOCK_MONOTONIC, timestamp);
} else if (timestamp != NULL) {
*timestamp = writeAt;
}
AHAL_VERBOSE("signed frames %lld written frames %lld kernel frames %lld dsp frames %lld, bt extra frames %lld",
(long long)signed_frames, (long long)written_frames, (long long)kernel_frames,
(long long)dsp_frames, (long long)bt_extra_frames);
return signed_frames;
}
int StreamOutPrimary::get_compressed_buffer_size()
{
char value[PROPERTY_VALUE_MAX] = {0};
int fragment_size = COMPRESS_OFFLOAD_FRAGMENT_SIZE;
int fsize = 0;
AHAL_DBG("config_ %x", config_.format);
if(config_.format == AUDIO_FORMAT_FLAC ) {
fragment_size = FLAC_COMPRESS_OFFLOAD_FRAGMENT_SIZE;
AHAL_DBG("aud_fmt_id: 0x%x FLAC buffer size:%d",
streamAttributes_.out_media_config.aud_fmt_id,
fragment_size);
} else {
fragment_size = COMPRESS_OFFLOAD_FRAGMENT_SIZE;
}
if((property_get("vendor.audio.offload.buffer.size.kb", value, "")) &&
atoi(value)) {
fsize = atoi(value) * 1024;
}
if (fsize > fragment_size)
fragment_size = fsize;
return fragment_size;
}
int StreamOutPrimary::get_pcm_buffer_size()
{
uint8_t channels = audio_channel_count_from_out_mask(config_.channel_mask);
uint8_t bytes_per_sample = audio_bytes_per_sample(config_.format);
audio_format_t src_format = config_.format;
audio_format_t dst_format = (audio_format_t)(getAlsaSupportedFmt.at(src_format));
uint32_t hal_op_bytes_per_sample = audio_bytes_per_sample(dst_format);
uint32_t hal_ip_bytes_per_sample = audio_bytes_per_sample(src_format);
uint32_t fragment_size = 0;
AHAL_DBG("config_ format:%x, SR %d ch_mask 0x%x, out format:%x",
config_.format, config_.sample_rate,
config_.channel_mask, dst_format);
fragment_size = PCM_OFFLOAD_OUTPUT_PERIOD_DURATION *
config_.sample_rate * bytes_per_sample * channels;
fragment_size /= 1000;
if (fragment_size < MIN_PCM_FRAGMENT_SIZE)
fragment_size = MIN_PCM_FRAGMENT_SIZE;
else if (fragment_size > MAX_PCM_FRAGMENT_SIZE)
fragment_size = MAX_PCM_FRAGMENT_SIZE;
fragment_size = ALIGN(fragment_size, (bytes_per_sample * channels * 32));
if ((src_format != dst_format) &&
hal_op_bytes_per_sample != hal_ip_bytes_per_sample) {
fragment_size =
(fragment_size * hal_ip_bytes_per_sample) /
hal_op_bytes_per_sample;
AHAL_INFO("enable conversion hal_input_fragment_size: src_format %x dst_format %x",
src_format, dst_format);
}
AHAL_DBG("fragment size: %d", fragment_size);
return fragment_size;
}
bool StreamOutPrimary:: period_size_is_plausible_for_low_latency(int period_size)
{
switch (period_size) {
case LL_PERIOD_SIZE_FRAMES_160:
case LL_PERIOD_SIZE_FRAMES_192:
case LL_PERIOD_SIZE_FRAMES_240:
case LL_PERIOD_SIZE_FRAMES_320:
case LL_PERIOD_SIZE_FRAMES_480:
return true;
default:
return false;
}
}
uint32_t StreamOutPrimary::GetBufferSizeForLowLatency() {
int trial = 0;
char value[PROPERTY_VALUE_MAX] = {0};
int configured_low_latency_period_size = LOW_LATENCY_PLAYBACK_PERIOD_SIZE;
if (property_get("vendor.audio_hal.period_size", value, NULL) > 0) {
trial = atoi(value);
if (period_size_is_plausible_for_low_latency(trial))
configured_low_latency_period_size = trial;
}
return configured_low_latency_period_size *
audio_bytes_per_frame(
audio_channel_count_from_out_mask(config_.channel_mask),
config_.format);
}
uint32_t StreamOutPrimary::GetBufferSize() {
struct pal_stream_attributes streamAttributes_;
streamAttributes_.type = StreamOutPrimary::GetPalStreamType(flags_);
AHAL_DBG("type %d", streamAttributes_.type);
if (streamAttributes_.type == PAL_STREAM_VOIP_RX) {
return (DEFAULT_VOIP_BUF_DURATION_MS * config_.sample_rate / 1000) *
audio_bytes_per_frame(
audio_channel_count_from_out_mask(config_.channel_mask),
config_.format);
} else if (streamAttributes_.type == PAL_STREAM_COMPRESSED) {
return get_compressed_buffer_size();
} else if (streamAttributes_.type == PAL_STREAM_PCM_OFFLOAD) {
return get_pcm_buffer_size();
} else if (streamAttributes_.type == PAL_STREAM_LOW_LATENCY) {
return GetBufferSizeForLowLatency();
} else if (streamAttributes_.type == PAL_STREAM_ULTRA_LOW_LATENCY) {
return ULL_PERIOD_SIZE * ULL_PERIOD_MULTIPLIER *
audio_bytes_per_frame(
audio_channel_count_from_out_mask(config_.channel_mask),
config_.format);
} else if (streamAttributes_.type == PAL_STREAM_DEEP_BUFFER){
return DEEP_BUFFER_PLAYBACK_PERIOD_SIZE *
audio_bytes_per_frame(
audio_channel_count_from_out_mask(config_.channel_mask),
config_.format);
} else {
return BUF_SIZE_PLAYBACK * NO_OF_BUF;
}
}
int StreamOutPrimary::Open() {
int ret = -EINVAL;
uint8_t channels = 0;
struct pal_channel_info ch_info = {0, {0}};
uint32_t outBufSize = 0;
uint32_t outBufCount = NO_OF_BUF;
struct pal_buffer_config outBufCfg = {0, 0, 0};
pal_param_device_capability_t *device_cap_query = NULL;
size_t payload_size = 0;
dynamic_media_config_t dynamic_media_config;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
bool isHifiFilterEnabled = false;
bool *payload_hifiFilter = &isHifiFilterEnabled;
size_t param_size = 0;
AHAL_INFO("Enter: OutPrimary usecase(%d: %s)", GetUseCase(), use_case_table[GetUseCase()]);
if (!mInitialized) {
AHAL_ERR("Not initialized, returning error");
goto error_open;
}
AHAL_DBG("no_of_devices %zu", mAndroidOutDevices.size());
//need to convert channel mask to pal channel mask
// Stream channel mask
channels = audio_channel_count_from_out_mask(config_.channel_mask);
if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS) {
channels = audio_channel_count_from_out_mask(config_.channel_mask & ~AUDIO_CHANNEL_HAPTIC_ALL);
}
ch_info.channels = channels;
ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
if (ch_info.channels > 1)
ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
streamAttributes_.type = StreamOutPrimary::GetPalStreamType(flags_);
streamAttributes_.flags = (pal_stream_flags_t)0;
streamAttributes_.direction = PAL_AUDIO_OUTPUT;
streamAttributes_.out_media_config.sample_rate = config_.sample_rate;
streamAttributes_.out_media_config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
streamAttributes_.out_media_config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE;
streamAttributes_.out_media_config.ch_info = ch_info;
switch(streamAttributes_.type) {
case PAL_STREAM_COMPRESSED:
streamAttributes_.flags = (pal_stream_flags_t)(PAL_STREAM_FLAG_NON_BLOCKING);
if (config_.offload_info.format == 0)
config_.offload_info.format = config_.format;
if (config_.offload_info.sample_rate == 0)
config_.offload_info.sample_rate = config_.sample_rate;
streamAttributes_.out_media_config.sample_rate = config_.offload_info.sample_rate;
if (msample_rate)
streamAttributes_.out_media_config.sample_rate = msample_rate;
if (mchannels)
streamAttributes_.out_media_config.ch_info.channels = mchannels;
if (getAlsaSupportedFmt.find(config_.format) != getAlsaSupportedFmt.end()) {
halInputFormat = config_.format;
halOutputFormat = (audio_format_t)(getAlsaSupportedFmt.at(config_.format));
streamAttributes_.out_media_config.aud_fmt_id = getFormatId.at(halOutputFormat);
streamAttributes_.out_media_config.bit_width = format_to_bitwidth_table[halOutputFormat];
if (streamAttributes_.out_media_config.bit_width == 0)
streamAttributes_.out_media_config.bit_width = 16;
streamAttributes_.type = PAL_STREAM_PCM_OFFLOAD;
} else {
streamAttributes_.out_media_config.aud_fmt_id = getFormatId.at(config_.format & AUDIO_FORMAT_MAIN_MASK);
}
break;
case PAL_STREAM_LOW_LATENCY:
case PAL_STREAM_ULTRA_LOW_LATENCY:
case PAL_STREAM_DEEP_BUFFER:
case PAL_STREAM_GENERIC:
case PAL_STREAM_PCM_OFFLOAD:
halInputFormat = config_.format;
halOutputFormat = (audio_format_t)(getAlsaSupportedFmt.at(halInputFormat));
streamAttributes_.out_media_config.aud_fmt_id = getFormatId.at(halOutputFormat);
streamAttributes_.out_media_config.bit_width = format_to_bitwidth_table[halOutputFormat];
AHAL_DBG("halInputFormat %d halOutputFormat %d palformat %d", halInputFormat,
halOutputFormat, streamAttributes_.out_media_config.aud_fmt_id);
if (streamAttributes_.out_media_config.bit_width == 0)
streamAttributes_.out_media_config.bit_width = 16;
if (streamAttributes_.type == PAL_STREAM_ULTRA_LOW_LATENCY) {
if (usecase_ == USECASE_AUDIO_PLAYBACK_MMAP) {
streamAttributes_.flags = (pal_stream_flags_t)(PAL_STREAM_FLAG_MMAP_NO_IRQ);
} else if (usecase_ == USECASE_AUDIO_PLAYBACK_ULL) {
streamAttributes_.flags = (pal_stream_flags_t)(PAL_STREAM_FLAG_MMAP);
}
}
break;
default:
break;
}
ret = pal_get_param(PAL_PARAM_ID_HIFI_PCM_FILTER,
(void **)&payload_hifiFilter, &param_size, nullptr);
if (!ret && isHifiFilterEnabled &&
(mPalOutDevice->id == PAL_DEVICE_OUT_WIRED_HEADSET ||
mPalOutDevice->id == PAL_DEVICE_OUT_WIRED_HEADPHONE) &&
(streamAttributes_.out_media_config.sample_rate != 384000 &&
streamAttributes_.out_media_config.sample_rate != 352800)) {
AHAL_DBG("hifi-filter custom key sent to PAL (only applicable to certain streams)\n");
strlcat(mPalOutDevice->custom_config.custom_key,
"hifi-filter_custom_key;",
sizeof(mPalOutDevice->custom_config.custom_key));
}
device_cap_query = (pal_param_device_capability_t *)malloc(sizeof(pal_param_device_capability_t));
if ((mPalOutDevice->id == PAL_DEVICE_OUT_USB_DEVICE || mPalOutDevice->id ==
PAL_DEVICE_OUT_USB_HEADSET) && device_cap_query && adevice) {
device_cap_query->id = mPalOutDevice->id;
device_cap_query->addr.card_id = adevice->usb_card_id_;
device_cap_query->addr.device_num = adevice->usb_dev_num_;
device_cap_query->config = &dynamic_media_config;
device_cap_query->is_playback = true;
ret = pal_get_param(PAL_PARAM_ID_DEVICE_CAPABILITY,(void **)&device_cap_query,
&payload_size, nullptr);
if (ret<0) {
AHAL_DBG("Error usb device is not connected");
ret = -ENOSYS;
goto error_open;
}
}
if (adevice->hac_voip && (mPalOutDevice->id == PAL_DEVICE_OUT_HANDSET)) {
strlcat(mPalOutDevice->custom_config.custom_key, "HAC;",
sizeof(mPalOutDevice->custom_config.custom_key));
}
AHAL_DBG("channels %d samplerate %d format id %d, stream type %d stream bitwidth %d",
streamAttributes_.out_media_config.ch_info.channels, streamAttributes_.out_media_config.sample_rate,
streamAttributes_.out_media_config.aud_fmt_id, streamAttributes_.type,
streamAttributes_.out_media_config.bit_width);
AHAL_DBG("msample_rate %d mchannels %d mNoOfOutDevices %zu", msample_rate, mchannels, mAndroidOutDevices.size());
ret = pal_stream_open(&streamAttributes_,
mAndroidOutDevices.size(),
mPalOutDevice,
0,
NULL,
&pal_callback,
(uint64_t)this,
&pal_stream_handle_);
if (ret) {
AHAL_ERR("Pal Stream Open Error (%x)", ret);
ret = -EINVAL;
goto error_open;
}
/* set cached volume if any, dont return failure back up */
if (volume_) {
AHAL_DBG("set cached volume (%f)", volume_->volume_pair[0].vol);
ret = pal_stream_set_volume(pal_stream_handle_, volume_);
if (ret) {
AHAL_ERR("Pal Stream volume Error (%x)", ret);
}
}
if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS) {
ch_info.channels = audio_channel_count_from_out_mask(config_.channel_mask & AUDIO_CHANNEL_HAPTIC_ALL);
ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
if (ch_info.channels > 1)
ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
hapticsStreamAttributes.type = PAL_STREAM_HAPTICS;
hapticsStreamAttributes.flags = (pal_stream_flags_t)0;
hapticsStreamAttributes.direction = PAL_AUDIO_OUTPUT;
hapticsStreamAttributes.out_media_config.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
hapticsStreamAttributes.out_media_config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
hapticsStreamAttributes.out_media_config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE;
hapticsStreamAttributes.out_media_config.ch_info = ch_info;
if (!hapticsDevice) {
hapticsDevice = (struct pal_device*) calloc(1, sizeof(struct pal_device));
}
if (hapticsDevice) {
hapticsDevice->id = PAL_DEVICE_OUT_HAPTICS_DEVICE;
hapticsDevice->config.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
hapticsDevice->config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
hapticsDevice->config.ch_info = ch_info;
hapticsDevice->config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE;
ret = pal_stream_open (&hapticsStreamAttributes,
1,
hapticsDevice,
0,
NULL,
&pal_callback,
(uint64_t)this,
&pal_haptics_stream_handle);
if (ret)
AHAL_ERR("Pal Haptics Stream Open Error (%x)", ret);
} else {
AHAL_ERR("Failed to allocate memory for hapticsDevice");
}
}
if (karaoke) {
ret = AudExtn.karaoke_open(mPalOutDevice[mAndroidOutDevices.size()-1].id, &pal_callback, ch_info);
if (ret) {
AHAL_ERR("Karaoke Open Error (%x)", ret);
karaoke = false;
}
}
//TODO: Remove below code, once pal_stream_open is moved to
//adev_open_output_stream
if (streamAttributes_.type == PAL_STREAM_COMPRESSED) {
pal_param_payload *param_payload = nullptr;
param_payload = (pal_param_payload *) calloc (1,
sizeof(pal_param_payload) +
sizeof(pal_snd_dec_t));
if (!param_payload) {
AHAL_ERR("calloc failed for size %zu",
sizeof(pal_param_payload) + sizeof(pal_snd_dec_t));
} else {
param_payload->payload_size = sizeof(pal_snd_dec_t);
memcpy(param_payload->payload, &palSndDec, param_payload->payload_size);
ret = pal_stream_set_param(pal_stream_handle_,
PAL_PARAM_ID_CODEC_CONFIGURATION,
param_payload);
if (ret)
AHAL_ERR("Pal Set Param Error (%x)", ret);
free(param_payload);
}
isCompressMetadataAvail = false;
}
if (usecase_ == USECASE_AUDIO_PLAYBACK_MMAP) {
outBufSize = MMAP_PERIOD_SIZE * audio_bytes_per_frame(
audio_channel_count_from_out_mask(config_.channel_mask),
config_.format);
outBufCount = MMAP_PERIOD_COUNT_DEFAULT;
} else if (usecase_ == USECASE_AUDIO_PLAYBACK_ULL) {
outBufSize = ULL_PERIOD_SIZE * audio_bytes_per_frame(
audio_channel_count_from_out_mask(config_.channel_mask),
config_.format);
outBufCount = ULL_PERIOD_COUNT_DEFAULT;
} else if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS) {
outBufSize = LOW_LATENCY_PLAYBACK_PERIOD_SIZE * audio_bytes_per_frame(
channels,
config_.format);
outBufCount = LOW_LATENCY_PLAYBACK_PERIOD_COUNT;
} else
outBufSize = StreamOutPrimary::GetBufferSize();
if (usecase_ == USECASE_AUDIO_PLAYBACK_LOW_LATENCY) {
if (streamAttributes_.type == PAL_STREAM_VOICE_CALL_MUSIC) {
outBufCount = LOW_LATENCY_ICMD_PLAYBACK_PERIOD_COUNT;
AHAL_DBG("LOW_LATENCY_ICMD - Buffer Count : %d", outBufCount);
}
else {
outBufCount = LOW_LATENCY_PLAYBACK_PERIOD_COUNT;
}
}
else if (usecase_ == USECASE_AUDIO_PLAYBACK_OFFLOAD2)
outBufCount = PCM_OFFLOAD_PLAYBACK_PERIOD_COUNT;
else if (usecase_ == USECASE_AUDIO_PLAYBACK_DEEP_BUFFER)
outBufCount = DEEP_BUFFER_PLAYBACK_PERIOD_COUNT;
else if (usecase_ == USECASE_AUDIO_PLAYBACK_VOIP)
outBufCount = VOIP_PERIOD_COUNT_DEFAULT;
if (halInputFormat != halOutputFormat) {
convertBuffer = realloc(convertBuffer, outBufSize);
if (!convertBuffer) {
ret = -ENOMEM;
AHAL_ERR("convert Buffer allocation failed. ret %d", ret);
goto error_open;
}
AHAL_DBG("convert buffer allocated for size %d", convertBufSize);
}
fragment_size_ = outBufSize;
fragments_ = outBufCount;
AHAL_DBG("fragment_size_ %d fragments_ %d", fragment_size_, fragments_);
outBufCfg.buf_size = fragment_size_;
outBufCfg.buf_count = fragments_;
ret = pal_stream_set_buffer_size(pal_stream_handle_, NULL, &outBufCfg);
if (ret) {
AHAL_ERR("Pal Stream set buffer size Error (%x)", ret);
}
if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS &&
pal_haptics_stream_handle) {
outBufSize = LOW_LATENCY_PLAYBACK_PERIOD_SIZE * audio_bytes_per_frame(
hapticsStreamAttributes.out_media_config.ch_info.channels,
config_.format);
outBufCount = LOW_LATENCY_PLAYBACK_PERIOD_COUNT;
fragment_size_ += outBufSize;
AHAL_DBG("fragment_size_ %d fragments_ %d", fragment_size_, fragments_);
outBufCfg.buf_size = outBufSize;
outBufCfg.buf_count = fragments_;
ret = pal_stream_set_buffer_size(pal_haptics_stream_handle, NULL, &outBufCfg);
if (ret) {
AHAL_ERR("Pal Stream set buffer size Error (%x)", ret);
}
}
error_open:
if (device_cap_query) {
free(device_cap_query);
device_cap_query = NULL;
}
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int StreamOutPrimary::GetFrames(uint64_t *frames)
{
int ret = 0;
pal_session_time tstamp;
uint64_t timestamp = 0;
uint64_t dsp_frames = 0;
uint64_t offset = 0;
size_t size = 0;
pal_param_bta2dp_t *param_bt_a2dp = NULL;
if (!pal_stream_handle_) {
AHAL_VERBOSE("pal_stream_handle_ NULL");
*frames = 0;
return 0;
}
if (!stream_started_) {
AHAL_VERBOSE("stream not in started state");
*frames = 0;
return 0;
}
ret = pal_get_timestamp(pal_stream_handle_, &tstamp);
if (ret != 0) {
AHAL_ERR("pal_get_timestamp failed %d", ret);
goto exit;
}
timestamp = (uint64_t)tstamp.session_time.value_msw;
timestamp = timestamp << 32 | tstamp.session_time.value_lsw;
AHAL_VERBOSE("session msw %u", tstamp.session_time.value_msw);
AHAL_VERBOSE("session lsw %u", tstamp.session_time.value_lsw);
AHAL_VERBOSE("session timespec %lld", ((long long) timestamp));
dsp_frames = timestamp / 1000
* streamAttributes_.out_media_config.sample_rate / 1000;
// Adjustment accounts for A2dp encoder latency with offload usecases
// Note: Encoder latency is returned in ms.
if (isDeviceAvailable(PAL_DEVICE_OUT_BLUETOOTH_A2DP)) {
ret = pal_get_param(PAL_PARAM_ID_BT_A2DP_ENCODER_LATENCY,
(void **)&param_bt_a2dp, &size, nullptr);
if (!ret && param_bt_a2dp) {
offset = param_bt_a2dp->latency *
(streamAttributes_.out_media_config.sample_rate) / 1000;
dsp_frames = (dsp_frames > offset) ? (dsp_frames - offset) : 0;
}
}
*frames = dsp_frames + mCachedPosition;
exit:
return ret;
}
int StreamOutPrimary::GetOutputUseCase(audio_output_flags_t halStreamFlags)
{
// TODO: just covered current supported usecases in PAL
// need to update other usecases in future
int usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
if (halStreamFlags & AUDIO_OUTPUT_FLAG_VOIP_RX)
usecase = USECASE_AUDIO_PLAYBACK_VOIP;
else if ((halStreamFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) ||
(halStreamFlags == AUDIO_OUTPUT_FLAG_DIRECT)) {
if (halStreamFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
usecase = USECASE_AUDIO_PLAYBACK_OFFLOAD;
else
usecase = USECASE_AUDIO_PLAYBACK_OFFLOAD2;
} else if (halStreamFlags & AUDIO_OUTPUT_FLAG_RAW)
usecase = USECASE_AUDIO_PLAYBACK_ULL;
else if (halStreamFlags & AUDIO_OUTPUT_FLAG_FAST)
usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
else if (halStreamFlags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER)
usecase = USECASE_AUDIO_PLAYBACK_DEEP_BUFFER;
else if (halStreamFlags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ)
usecase = USECASE_AUDIO_PLAYBACK_MMAP;
else if (config_.channel_mask & AUDIO_CHANNEL_HAPTIC_ALL)
usecase = USECASE_AUDIO_PLAYBACK_WITH_HAPTICS;
return usecase;
}
ssize_t StreamOutPrimary::splitAndWriteAudioHapticsStream(const void *buffer, size_t bytes)
{
ssize_t ret = 0;
bool allocHapticsBuffer = false;
struct pal_buffer audioBuf;
struct pal_buffer hapticBuf;
size_t srcIndex = 0, audIndex = 0, hapIndex = 0;
uint8_t channelCount = audio_channel_count_from_out_mask(config_.channel_mask);
uint8_t bytesPerSample = audio_bytes_per_sample(config_.format);
uint32_t frameSize = channelCount * bytesPerSample;
uint32_t frameCount = bytes / frameSize;
// Calculate Haptics Buffer size
uint8_t hapticsChannelCount = hapticsStreamAttributes.out_media_config.ch_info.channels;
uint32_t hapticsFrameSize = bytesPerSample * hapticsChannelCount;
uint32_t audioFrameSize = frameSize - hapticsFrameSize;
uint32_t totalHapticsBufferSize = frameCount * hapticsFrameSize;
if (!hapticBuffer) {
allocHapticsBuffer = true;
} else if (hapticsBufSize < totalHapticsBufferSize) {
if (hapticBuffer)
free (hapticBuffer);
allocHapticsBuffer = true;
hapticsBufSize = 0;
}
if (allocHapticsBuffer) {
hapticBuffer = (uint8_t *)calloc(1, totalHapticsBufferSize);
if(!hapticBuffer) {
AHAL_ERR("Failed to allocate mem for haptic buffer");
return -ENOMEM;
}
hapticsBufSize = totalHapticsBufferSize;
}
audioBuf.buffer = (uint8_t *)buffer;
audioBuf.size = frameCount * audioFrameSize;
audioBuf.offset = 0;
hapticBuf.buffer = hapticBuffer;
hapticBuf.size = frameCount * hapticsFrameSize;
hapticBuf.offset = 0;
for (size_t i = 0; i < frameCount; i++) {
memcpy((uint8_t *)(audioBuf.buffer) + audIndex, (uint8_t *)(audioBuf.buffer) + srcIndex,
audioFrameSize);
audIndex += audioFrameSize;
srcIndex += audioFrameSize;
memcpy((uint8_t *)(hapticBuf.buffer) + hapIndex, (uint8_t *)(audioBuf.buffer) + srcIndex,
hapticsFrameSize);
hapIndex += hapticsFrameSize;
srcIndex += hapticsFrameSize;
}
// write audio data
ret = pal_stream_write(pal_stream_handle_, &audioBuf);
// write haptics data
ret = pal_stream_write(pal_haptics_stream_handle, &hapticBuf);
return (ret < 0 ? ret : bytes);
}
ssize_t StreamOutPrimary::onWriteError(size_t bytes, ssize_t ret) {
// standby streams upon write failures and sleep for buffer duration.
AHAL_ERR("write error %d usecase(%d: %s)", ret, GetUseCase(), use_case_table[GetUseCase()]);
Standby();
if (streamAttributes_.type != PAL_STREAM_COMPRESSED) {
uint32_t byteWidth = streamAttributes_.out_media_config.bit_width / 8;
uint32_t sampleRate = streamAttributes_.out_media_config.sample_rate;
uint32_t channelCount = streamAttributes_.out_media_config.ch_info.channels;
uint32_t frameSize = byteWidth * channelCount;
if (frameSize == 0 || sampleRate == 0) {
AHAL_ERR("invalid frameSize=%d, sampleRate=%d", frameSize, sampleRate);
return -EINVAL;
} else {
usleep((uint64_t)bytes * 1000000 / frameSize / sampleRate);
return bytes;
}
}
// Return error in case of compress offload.
return ret;
}
ssize_t StreamOutPrimary::configurePalOutputStream() {
ssize_t ret = 0;
if (!pal_stream_handle_) {
AutoPerfLock perfLock;
ATRACE_BEGIN("hal:open_output");
ret = Open();
ATRACE_END();
if (ret) {
AHAL_ERR("failed to open stream.");
return -EINVAL;
}
}
if (!stream_started_) {
AutoPerfLock perfLock;
ATRACE_BEGIN("hal: pal_stream_start");
ret = pal_stream_start(pal_stream_handle_);
if (ret) {
AHAL_ERR("failed to start stream. ret=%d", ret);
pal_stream_close(pal_stream_handle_);
pal_stream_handle_ = NULL;
ATRACE_END();
if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS &&
pal_haptics_stream_handle) {
AHAL_DBG("Close haptics stream");
pal_stream_close(pal_haptics_stream_handle);
pal_haptics_stream_handle = NULL;
}
return -EINVAL;
} else {
AHAL_INFO("notify GEF client of device config");
for(auto dev : mAndroidOutDevices)
audio_extn_gef_notify_device_config(dev, config_.channel_mask,
config_.sample_rate, flags_);
}
if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS) {
ret = pal_stream_start(pal_haptics_stream_handle);
if (ret) {
AHAL_ERR("failed to start haptics stream. ret=%d", ret);
ATRACE_END();
pal_stream_close(pal_haptics_stream_handle);
pal_haptics_stream_handle = NULL;
return -EINVAL;
}
}
if (karaoke) {
ret = AudExtn.karaoke_start();
if (ret) {
AHAL_ERR("failed to start karaoke stream. ret=%d", ret);
AudExtn.karaoke_close();
karaoke = false;
ret = 0; // Not fatal error
}
}
stream_started_ = true;
if (CheckOffloadEffectsType(streamAttributes_.type)) {
ret = StartOffloadEffects(handle_, pal_stream_handle_);
ret = StartOffloadVisualizer(handle_, pal_stream_handle_);
}
ATRACE_END();
}
if ((streamAttributes_.type == PAL_STREAM_COMPRESSED) && isCompressMetadataAvail) {
// Send codec params first.
pal_param_payload *param_payload = nullptr;
param_payload = (pal_param_payload *) calloc (1,
sizeof(pal_param_payload) +
sizeof(pal_snd_dec_t));
if (param_payload) {
param_payload->payload_size = sizeof(pal_snd_dec_t);
memcpy(param_payload->payload, &palSndDec, param_payload->payload_size);
ret = pal_stream_set_param(pal_stream_handle_,
PAL_PARAM_ID_CODEC_CONFIGURATION,
param_payload);
if (ret) {
AHAL_INFO("Pal Set Param for codec configuration failed (%x)", ret);
ret = 0;
}
free(param_payload);
} else {
AHAL_ERR("calloc failed for size %zu",
sizeof(pal_param_payload) + sizeof(pal_snd_dec_t));
}
isCompressMetadataAvail = false;
}
if ((streamAttributes_.type == PAL_STREAM_COMPRESSED) && sendGaplessMetadata) {
//Send gapless metadata
pal_param_payload *param_payload = nullptr;
param_payload = (pal_param_payload *) calloc (1,
sizeof(pal_param_payload) +
sizeof(struct pal_compr_gapless_mdata));
if (param_payload) {
AHAL_DBG("sending gapless metadata");
param_payload->payload_size = sizeof(struct pal_compr_gapless_mdata);
memcpy(param_payload->payload, &gaplessMeta, param_payload->payload_size);
ret = pal_stream_set_param(pal_stream_handle_,
PAL_PARAM_ID_GAPLESS_MDATA,
param_payload);
if (ret) {
AHAL_INFO("PAL set param for gapless failed, error (%x)", ret);
ret = 0;
}
free(param_payload);
} else {
AHAL_ERR("Failed to allocate gapless payload");
}
sendGaplessMetadata = false;
}
return 0;
}
ssize_t StreamOutPrimary::write(const void *buffer, size_t bytes)
{
ssize_t ret = 0;
struct pal_buffer palBuffer;
uint32_t frames;
palBuffer.buffer = (uint8_t*)buffer;
palBuffer.size = bytes;
palBuffer.offset = 0;
AHAL_VERBOSE("handle_ %x bytes:(%zu)", handle_, bytes);
stream_mutex_.lock();
ret = configurePalOutputStream();
if (ret < 0)
goto exit;
ATRACE_BEGIN("hal: pal_stream_write");
if (halInputFormat != halOutputFormat && convertBuffer != NULL) {
if (bytes > fragment_size_) {
AHAL_ERR("Error written bytes %zu > %d (fragment_size)", bytes, fragment_size_);
ATRACE_END();
stream_mutex_.unlock();
return -EINVAL;
}
/* prevent division-by-zero */
uint32_t inputBitWidth = format_to_bitwidth_table[halInputFormat];
uint32_t outputBitWidth = format_to_bitwidth_table[halOutputFormat];
if (inputBitWidth == 0 || outputBitWidth == 0) {
AHAL_ERR("Error inputBitWidth %u, outputBitWidth %u", inputBitWidth, outputBitWidth);
ATRACE_END();
stream_mutex_.unlock();
return -EINVAL;
}
frames = bytes / (inputBitWidth / 8);
memcpy_by_audio_format(convertBuffer, halOutputFormat, buffer, halInputFormat, frames);
palBuffer.buffer = (uint8_t *)convertBuffer;
palBuffer.size = frames * (outputBitWidth / 8);
ret = pal_stream_write(pal_stream_handle_, &palBuffer);
if (ret >= 0) {
ret = (ret * inputBitWidth) / outputBitWidth;
}
} else if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS && pal_haptics_stream_handle) {
ret = splitAndWriteAudioHapticsStream(buffer, bytes);
} else {
ret = pal_stream_write(pal_stream_handle_, &palBuffer);
}
ATRACE_END();
exit:
if (mBytesWritten <= UINT64_MAX - bytes) {
mBytesWritten += bytes;
} else {
mBytesWritten = UINT64_MAX;
}
stream_mutex_.unlock();
clock_gettime(CLOCK_MONOTONIC, &writeAt);
return (ret < 0 ? onWriteError(bytes, ret) : ret);
}
bool StreamOutPrimary::CheckOffloadEffectsType(pal_stream_type_t pal_stream_type) {
if (pal_stream_type == PAL_STREAM_COMPRESSED ||
pal_stream_type == PAL_STREAM_PCM_OFFLOAD) {
return true;
}
return false;
}
int StreamOutPrimary::StartOffloadEffects(
audio_io_handle_t ioHandle,
pal_stream_handle_t* pal_stream_handle) {
int ret = 0;
if (fnp_offload_effect_start_output_) {
ret = fnp_offload_effect_start_output_(ioHandle, pal_stream_handle);
if (ret) {
AHAL_ERR("failed to start offload effect.");
}
} else {
AHAL_ERR("error function pointer is null.");
return -EINVAL;
}
return ret;
}
int StreamOutPrimary::StopOffloadEffects(
audio_io_handle_t ioHandle,
pal_stream_handle_t* pal_stream_handle) {
int ret = 0;
if (fnp_offload_effect_stop_output_) {
ret = fnp_offload_effect_stop_output_(ioHandle, pal_stream_handle);
if (ret) {
AHAL_ERR("failed to stop offload effect.\n");
}
} else {
AHAL_ERR("error function pointer is null.");
return -EINVAL;
}
return ret;
}
int StreamOutPrimary::StartOffloadVisualizer(
audio_io_handle_t ioHandle,
pal_stream_handle_t* pal_stream_handle) {
int ret = 0;
if (fnp_visualizer_start_output_) {
ret = fnp_visualizer_start_output_(ioHandle, pal_stream_handle);
if (ret) {
AHAL_ERR("failed to visualizer_start.");
}
} else {
AHAL_ERR("function pointer is null.");
return -EINVAL;
}
return ret;
}
int StreamOutPrimary::StopOffloadVisualizer(
audio_io_handle_t ioHandle,
pal_stream_handle_t* pal_stream_handle) {
int ret = 0;
if (fnp_visualizer_stop_output_) {
ret = fnp_visualizer_stop_output_(ioHandle, pal_stream_handle);
if (ret) {
AHAL_ERR("failed to visualizer_stop.\n");
}
} else {
AHAL_ERR("function pointer is null.");
return -EINVAL;
}
return ret;
}
StreamOutPrimary::StreamOutPrimary(
audio_io_handle_t handle,
const std::set<audio_devices_t> &devices,
audio_output_flags_t flags,
struct audio_config *config,
const char *address __unused,
offload_effects_start_output start_offload_effect,
offload_effects_stop_output stop_offload_effect,
visualizer_hal_start_output visualizer_start_output,
visualizer_hal_stop_output visualizer_stop_output):
StreamPrimary(handle, devices, config),
mAndroidOutDevices(devices),
flags_(flags)
{
stream_ = std::shared_ptr<audio_stream_out> (new audio_stream_out());
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
mInitialized = false;
pal_stream_handle_ = nullptr;
pal_haptics_stream_handle = nullptr;
mPalOutDeviceIds = nullptr;
mPalOutDevice = nullptr;
convertBuffer = NULL;
hapticsDevice = NULL;
hapticBuffer = NULL;
hapticsBufSize = 0;
writeAt.tv_sec = 0;
writeAt.tv_nsec = 0;
mBytesWritten = 0;
int noPalDevices = 0;
int ret = 0;
/*Initialize the gaplessMeta value with 0*/
memset(&gaplessMeta,0,sizeof(struct pal_compr_gapless_mdata));
if (!stream_) {
AHAL_ERR("No memory allocated for stream_");
throw std::runtime_error("No memory allocated for stream_");
}
AHAL_DBG("enter: handle (%x) format(%#x) sample_rate(%d) channel_mask(%#x) devices(%zu) flags(%#x)\
address(%s)", handle, config->format, config->sample_rate, config->channel_mask,
mAndroidOutDevices.size(), flags, address);
//TODO: check if USB device is connected or not
if (AudioExtn::audio_devices_cmp(mAndroidOutDevices, audio_is_usb_out_device)){
// get capability from device of USB
device_cap_query_ = (pal_param_device_capability_t *)
calloc(1, sizeof(pal_param_device_capability_t));
if (!device_cap_query_) {
AHAL_ERR("Failed to allocate mem for device_cap_query_");
goto error;
}
dynamic_media_config_t *dynamic_media_config = (dynamic_media_config_t *)
calloc(1, sizeof(dynamic_media_config_t));
if (!dynamic_media_config) {
free(device_cap_query_);
AHAL_ERR("Failed to allocate mem for dynamic_media_config");
goto error;
}
size_t payload_size = 0;
device_cap_query_->id = PAL_DEVICE_OUT_USB_DEVICE;
device_cap_query_->addr.card_id = adevice->usb_card_id_;
device_cap_query_->addr.device_num = adevice->usb_dev_num_;
device_cap_query_->config = dynamic_media_config;
device_cap_query_->is_playback = true;
ret = pal_get_param(PAL_PARAM_ID_DEVICE_CAPABILITY,
(void **)&device_cap_query_,
&payload_size, nullptr);
if (ret < 0) {
AHAL_ERR("Error usb device is not connected");
free(dynamic_media_config);
free(device_cap_query_);
dynamic_media_config = NULL;
device_cap_query_ = NULL;
}
if (!config->sample_rate || !config->format || !config->channel_mask) {
if (dynamic_media_config) {
config->sample_rate = dynamic_media_config->sample_rate[0];
config->channel_mask = (audio_channel_mask_t) dynamic_media_config->mask[0];
config->format = (audio_format_t)dynamic_media_config->format[0];
}
if (config->sample_rate == 0)
config->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
if (config->channel_mask == AUDIO_CHANNEL_NONE)
config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
if (config->format == AUDIO_FORMAT_DEFAULT)
config->format = AUDIO_FORMAT_PCM_16_BIT;
memcpy(&config_, config, sizeof(struct audio_config));
AHAL_INFO("sample rate = %#x channel_mask=%#x fmt=%#x",
config->sample_rate, config->channel_mask,
config->format);
}
}
if (AudioExtn::audio_devices_cmp(mAndroidOutDevices, AUDIO_DEVICE_OUT_AUX_DIGITAL)){
AHAL_DBG("AUDIO_DEVICE_OUT_AUX_DIGITAL and DIRECT | OFFLOAD, check hdmi caps");
if (config->sample_rate == 0) {
config->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
config_.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
}
if (config->channel_mask == AUDIO_CHANNEL_NONE) {
config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
config_.channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
}
if (config->format == AUDIO_FORMAT_DEFAULT) {
config->format = AUDIO_FORMAT_PCM_16_BIT;
config_.format = AUDIO_FORMAT_PCM_16_BIT;
}
}
usecase_ = GetOutputUseCase(flags);
if (address) {
strlcpy((char *)&address_, address, AUDIO_DEVICE_MAX_ADDRESS_LEN);
} else {
AHAL_DBG("invalid address");
}
fnp_offload_effect_start_output_ = start_offload_effect;
fnp_offload_effect_stop_output_ = stop_offload_effect;
fnp_visualizer_start_output_ = visualizer_start_output;
fnp_visualizer_stop_output_ = visualizer_stop_output;
if (mAndroidOutDevices.empty())
mAndroidOutDevices.insert(AUDIO_DEVICE_OUT_DEFAULT);
AHAL_DBG("No of Android devices %zu", mAndroidOutDevices.size());
mPalOutDeviceIds = (pal_device_id_t*) calloc(mAndroidOutDevices.size(), sizeof(pal_device_id_t));
if (!mPalOutDeviceIds) {
goto error;
}
noPalDevices = getPalDeviceIds(mAndroidOutDevices, mPalOutDeviceIds);
if (noPalDevices != mAndroidOutDevices.size()) {
AHAL_ERR("mismatched pal no of devices %d and hal devices %zu", noPalDevices, mAndroidOutDevices.size());
goto error;
}
mPalOutDevice = (struct pal_device*) calloc(mAndroidOutDevices.size(), sizeof(struct pal_device));
if (!mPalOutDevice) {
goto error;
}
/* TODO: how to update based on stream parameters and see if device is supported */
for (int i = 0; i < mAndroidOutDevices.size(); i++) {
memset(mPalOutDevice[i].custom_config.custom_key, 0, sizeof(mPalOutDevice[i].custom_config.custom_key));
mPalOutDevice[i].id = mPalOutDeviceIds[i];
if (AudioExtn::audio_devices_cmp(mAndroidOutDevices, audio_is_usb_out_device))
mPalOutDevice[i].config.sample_rate = config_.sample_rate;
else
mPalOutDevice[i].config.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
mPalOutDevice[i].config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
mPalOutDevice[i].config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE; // TODO: need to convert this from output format
AHAL_INFO("device rate = %#x width=%#x fmt=%#x",
mPalOutDevice[i].config.sample_rate,
mPalOutDevice[i].config.bit_width,
mPalOutDevice[i].config.aud_fmt_id);
mPalOutDevice[i].config.ch_info = {0, {0}};
if ((mPalOutDeviceIds[i] == PAL_DEVICE_OUT_USB_DEVICE) ||
(mPalOutDeviceIds[i] == PAL_DEVICE_OUT_USB_HEADSET)) {
mPalOutDevice[i].address.card_id = adevice->usb_card_id_;
mPalOutDevice[i].address.device_num = adevice->usb_dev_num_;
}
strlcpy(mPalOutDevice[i].custom_config.custom_key, "",
sizeof(mPalOutDevice[i].custom_config.custom_key));
if ((AudioExtn::audio_devices_cmp(mAndroidOutDevices, AUDIO_DEVICE_OUT_SPEAKER_SAFE)) &&
(mPalOutDeviceIds[i] == PAL_DEVICE_OUT_SPEAKER)) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "speaker-safe;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
AHAL_INFO("Setting custom key as %s", mPalOutDevice[i].custom_config.custom_key);
}
#ifdef DYNAMIC_SR_ENABLED
if (((usecase_ == USECASE_AUDIO_PLAYBACK_VOIP) ||
(usecase_ == USECASE_AUDIO_PLAYBACK_DEEP_BUFFER) ||
(isOffloadUsecase())) &&
((mPalOutDevice[i].id == PAL_DEVICE_OUT_SPEAKER) ||
(mPalOutDevice[i].id == PAL_DEVICE_OUT_HANDSET) ||
(mPalOutDevice[i].id == PAL_DEVICE_OUT_WIRED_HEADPHONE) ||
(mPalOutDevice[i].id == PAL_DEVICE_OUT_WIRED_HEADSET) ||
(mPalOutDevice[i].id == PAL_DEVICE_OUT_USB_DEVICE) ||
(mPalOutDevice[i].id == PAL_DEVICE_OUT_USB_HEADSET))) {
if (config_.sample_rate == 8000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "8K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 11025) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "11K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 16000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "16K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 22050) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "22K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 24000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "24K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 32000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "32K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 44100) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "44.1K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 48000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "48K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 64000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "64K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 88200) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "88.2K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 96000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "96K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 176400) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "176.4K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 192000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "192K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 352800) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "352.8K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 384000) {
strlcat(mPalOutDevice[i].custom_config.custom_key, "384K;",
sizeof(mPalOutDevice[i].custom_config.custom_key));
} else {
AHAL_DBG("No custom config to set for usecase %d for sr %d",
usecase_, config_.sample_rate);
}
AHAL_DBG("setting SR for usecase %d as %d", usecase_, config_.sample_rate);
}
#endif
}
if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
stream_.get()->start = astream_out_mmap_noirq_start;
stream_.get()->stop = astream_out_mmap_noirq_stop;
stream_.get()->create_mmap_buffer = astream_out_create_mmap_buffer;
stream_.get()->get_mmap_position = astream_out_get_mmap_position;
}
if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS) {
AHAL_INFO("Haptics Usecase");
/* Setting flag here as no flag is being set for haptics from AudioPolicyManager
* so that audio stream runs as low latency stream.
*/
flags_ = AUDIO_OUTPUT_FLAG_FAST;
}
mInitialized = true;
for(auto dev : mAndroidOutDevices)
audio_extn_gef_notify_device_config(dev, config_.channel_mask,
config_.sample_rate, flags_);
error:
(void)FillHalFnPtrs();
AHAL_DBG("Exit");
return;
}
StreamOutPrimary::~StreamOutPrimary() {
AHAL_DBG("close stream, handle(%x), pal_stream_handle (%p)",
handle_, pal_stream_handle_);
stream_mutex_.lock();
if (pal_stream_handle_) {
if (CheckOffloadEffectsType(streamAttributes_.type)) {
StopOffloadEffects(handle_, pal_stream_handle_);
StopOffloadVisualizer(handle_, pal_stream_handle_);
}
pal_stream_close(pal_stream_handle_);
pal_stream_handle_ = nullptr;
}
if (pal_haptics_stream_handle) {
pal_stream_close(pal_haptics_stream_handle);
pal_haptics_stream_handle = NULL;
if (hapticBuffer) {
free (hapticBuffer);
hapticBuffer = NULL;
}
hapticsBufSize = 0;
}
if (convertBuffer)
free(convertBuffer);
if (mPalOutDeviceIds) {
free(mPalOutDeviceIds);
mPalOutDeviceIds = NULL;
}
if (mPalOutDevice) {
free(mPalOutDevice);
mPalOutDevice = NULL;
}
if (hapticsDevice) {
free(hapticsDevice);
hapticsDevice = NULL;
}
stream_mutex_.unlock();
}
bool StreamInPrimary::isDeviceAvailable(pal_device_id_t deviceId)
{
for (int i = 0; i < mAndroidInDevices.size(); i++) {
if (mPalInDevice[i].id == deviceId)
return true;
}
return false;
}
int StreamInPrimary::GetPalDeviceIds(pal_device_id_t *palDevIds, int *numPalDevs)
{
int noPalDevices;
if (!palDevIds || !numPalDevs)
return -EINVAL;
noPalDevices = getPalDeviceIds(mAndroidInDevices, mPalInDeviceIds);
if (noPalDevices > MAX_ACTIVE_MICROPHONES_TO_SUPPORT)
return -EINVAL;
*numPalDevs = noPalDevices;
for(int i = 0; i < noPalDevices; i++)
palDevIds[i] = mPalInDeviceIds[i];
return 0;
}
int StreamInPrimary::Stop() {
int ret = -ENOSYS;
AHAL_INFO("Enter: InPrimary usecase(%d: %s)", GetUseCase(), use_case_table[GetUseCase()]);
stream_mutex_.lock();
if (usecase_ == USECASE_AUDIO_RECORD_MMAP &&
pal_stream_handle_ && stream_started_) {
ret = pal_stream_stop(pal_stream_handle_);
if (ret == 0)
stream_started_ = false;
}
stream_mutex_.unlock();
return ret;
}
int StreamInPrimary::Start() {
int ret = -ENOSYS;
AHAL_INFO("Enter: InPrimary usecase(%d: %s)", GetUseCase(), use_case_table[GetUseCase()]);
stream_mutex_.lock();
if (usecase_ == USECASE_AUDIO_RECORD_MMAP &&
pal_stream_handle_ && !stream_started_) {
ret = pal_stream_start(pal_stream_handle_);
if (ret == 0)
stream_started_ = true;
}
stream_mutex_.unlock();
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int StreamInPrimary::CreateMmapBuffer(int32_t min_size_frames,
struct audio_mmap_buffer_info *info)
{
int ret;
struct pal_mmap_buffer palMmapBuf;
stream_mutex_.lock();
if (pal_stream_handle_) {
AHAL_ERR("error pal handle already created\n");
stream_mutex_.unlock();
return -EINVAL;
}
ret = Open();
if (ret) {
AHAL_ERR("failed to open stream.");
stream_mutex_.unlock();
return ret;
}
ret = pal_stream_create_mmap_buffer(pal_stream_handle_,
min_size_frames, &palMmapBuf);
if (ret) {
AHAL_ERR("failed to create mmap buffer: %d", ret);
// release stream lock as Standby will lock/unlock stream mutex
stream_mutex_.unlock();
Standby();
return ret;
}
info->shared_memory_address = palMmapBuf.buffer;
info->shared_memory_fd = palMmapBuf.fd;
info->buffer_size_frames = palMmapBuf.buffer_size_frames;
info->burst_size_frames = palMmapBuf.burst_size_frames;
info->flags = (audio_mmap_buffer_flag)palMmapBuf.flags;
mmap_shared_memory_fd = info->shared_memory_fd;
stream_mutex_.unlock();
return ret;
}
int StreamInPrimary::GetMmapPosition(struct audio_mmap_position *position)
{
struct pal_mmap_position pal_mmap_pos;
int32_t ret = 0;
stream_mutex_.lock();
if (pal_stream_handle_ == nullptr) {
AHAL_ERR("error pal handle is null\n");
stream_mutex_.unlock();
return -EINVAL;
}
ret = pal_stream_get_mmap_position(pal_stream_handle_, &pal_mmap_pos);
if (ret) {
AHAL_ERR("failed to get mmap position %d\n", ret);
stream_mutex_.unlock();
return ret;
}
position->position_frames = pal_mmap_pos.position_frames;
position->time_nanoseconds = pal_mmap_pos.time_nanoseconds;
stream_mutex_.unlock();
return 0;
}
int StreamInPrimary::Standby() {
int ret = 0;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
AHAL_DBG("Enter");
stream_mutex_.lock();
if (pal_stream_handle_) {
if (!is_st_session) {
ret = pal_stream_stop(pal_stream_handle_);
} else if (audio_extn_sound_trigger_check_session_activity(this)) {
ret = pal_stream_set_param(pal_stream_handle_,
PAL_PARAM_ID_STOP_BUFFERING, nullptr);
if (adevice->num_va_sessions_ > 0) {
adevice->num_va_sessions_--;
}
}
}
effects_applied_ = true;
stream_started_ = false;
if (pal_stream_handle_ && !is_st_session) {
ret = pal_stream_close(pal_stream_handle_);
pal_stream_handle_ = NULL;
}
if (mmap_shared_memory_fd >= 0) {
close(mmap_shared_memory_fd);
mmap_shared_memory_fd = -1;
}
if (ret)
ret = -EINVAL;
stream_mutex_.unlock();
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int StreamInPrimary::addRemoveAudioEffect(const struct audio_stream *stream __unused,
effect_handle_t effect,
bool enable)
{
int status = 0;
effect_descriptor_t desc;
status = (*effect)->get_descriptor(effect, &desc);
if (status != 0)
return status;
if (source_ == AUDIO_SOURCE_VOICE_COMMUNICATION) {
if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
if (enable) {
if (isECEnabled) {
AHAL_ERR("EC already enabled");
goto exit;
} else if (isNSEnabled) {
AHAL_VERBOSE("Got EC enable and NS is already active. Enabling ECNS");
status = pal_add_remove_effect(pal_stream_handle_,PAL_AUDIO_EFFECT_ECNS,true);
isECEnabled = true;
goto exit;
} else {
AHAL_VERBOSE("Got EC enable. Enabling EC");
status = pal_add_remove_effect(pal_stream_handle_,PAL_AUDIO_EFFECT_EC,true);
isECEnabled = true;
goto exit;
}
} else {
if (isECEnabled) {
if (isNSEnabled) {
AHAL_VERBOSE("ECNS is running. Disabling EC and enabling NS alone");
status = pal_add_remove_effect(pal_stream_handle_,PAL_AUDIO_EFFECT_NS,true);
isECEnabled = false;
goto exit;
} else {
AHAL_VERBOSE("EC is running. Disabling it");
status = pal_add_remove_effect(pal_stream_handle_,PAL_AUDIO_EFFECT_ECNS,false);
isECEnabled = false;
goto exit;
}
} else {
AHAL_ERR("EC is not enabled");
goto exit;
}
}
}
if (memcmp(&desc.type, FX_IID_NS, sizeof(effect_uuid_t)) == 0) {
if (enable) {
if (isNSEnabled) {
AHAL_ERR("NS already enabled");
goto exit;
} else if (isECEnabled) {
AHAL_VERBOSE("Got NS enable and EC is already active. Enabling ECNS");
status = pal_add_remove_effect(pal_stream_handle_,PAL_AUDIO_EFFECT_ECNS,true);
isNSEnabled = true;
goto exit;
} else {
AHAL_VERBOSE("Got NS enable. Enabling NS");
status = pal_add_remove_effect(pal_stream_handle_,PAL_AUDIO_EFFECT_NS,true);
isNSEnabled = true;
goto exit;
}
} else {
if (isNSEnabled) {
if (isECEnabled) {
AHAL_VERBOSE("ECNS is running. Disabling NS and enabling EC alone");
status = pal_add_remove_effect(pal_stream_handle_,PAL_AUDIO_EFFECT_EC,true);
isNSEnabled = false;
goto exit;
} else {
AHAL_VERBOSE("NS is running. Disabling it");
status = pal_add_remove_effect(pal_stream_handle_,PAL_AUDIO_EFFECT_ECNS,false);
isNSEnabled = false;
goto exit;
}
} else {
AHAL_ERR("NS is not enabled");
goto exit;
}
}
}
}
exit:
if (status) {
effects_applied_ = false;
} else
effects_applied_ = true;
return 0;
}
int StreamInPrimary::SetGain(float gain) {
struct pal_volume_data* volume;
int ret = 0;
AHAL_DBG("Enter");
stream_mutex_.lock();
volume = (struct pal_volume_data*)malloc(sizeof(uint32_t)
+sizeof(struct pal_channel_vol_kv));
if (!volume) {
AHAL_ERR("Failed to allocate mem for volume");
ret = -ENOMEM;
goto done;
}
volume->no_of_volpair = 1;
volume->volume_pair[0].channel_mask = 0x03;
volume->volume_pair[0].vol = gain;
if (pal_stream_handle_) {
ret = pal_stream_set_volume(pal_stream_handle_, volume);
}
free(volume);
if (ret) {
AHAL_ERR("Pal Stream volume Error (%x)", ret);
}
done:
stream_mutex_.unlock();
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int StreamInPrimary::RouteStream(const std::set<audio_devices_t>& new_devices, bool force_device_switch) {
bool is_empty, is_input;
int ret = 0, noPalDevices = 0;
pal_device_id_t * deviceId = nullptr;
struct pal_device* deviceIdConfigs = nullptr;
pal_param_device_capability_t *device_cap_query = nullptr;
size_t payload_size = 0;
dynamic_media_config_t dynamic_media_config;
struct pal_channel_info ch_info = {0, {0}};
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
AHAL_INFO("Enter: InPrimary usecase(%d: %s)", GetUseCase(), use_case_table[GetUseCase()]);
stream_mutex_.lock();
if (!mInitialized){
AHAL_ERR("Not initialized, returning error");
ret = -EINVAL;
goto done;
}
AHAL_DBG("mAndroidInDevices 0x%x, mNoOfInDevices %zu, new_devices 0x%x, num new_devices: %zu",
AudioExtn::get_device_types(mAndroidInDevices),
mAndroidInDevices.size(), AudioExtn::get_device_types(new_devices), new_devices.size());
// TBD: Hard code number of channels to 2 for now.
// channels = audio_channel_count_from_out_mask(config_.channel_mask);
// need to convert channel mask to pal channel mask
ch_info.channels = 2;
ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
if (ch_info.channels > 1 )
ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
is_empty = AudioExtn::audio_devices_empty(new_devices);
is_input = AudioExtn::audio_devices_cmp(new_devices, audio_is_input_device);
/* If its the same device as what was already routed to, dont bother */
if (!is_empty && is_input
&& ((mAndroidInDevices != new_devices) || force_device_switch)) {
//re-allocate mPalInDevice and mPalInDeviceIds
if (new_devices.size() != mAndroidInDevices.size()) {
deviceId = (pal_device_id_t*) realloc(mPalInDeviceIds,
new_devices.size() * sizeof(pal_device_id_t));
deviceIdConfigs = (struct pal_device*) realloc(mPalInDevice,
new_devices.size() * sizeof(struct pal_device));
if (!deviceId || !deviceIdConfigs) {
AHAL_ERR("Failed to allocate PalOutDeviceIds or deviceIdConfigs!");
if (deviceId)
mPalInDeviceIds = deviceId;
if (deviceIdConfigs)
mPalInDevice = deviceIdConfigs;
ret = -ENOMEM;
goto done;
}
// init deviceId and deviceIdConfigs
memset(deviceId, 0, new_devices.size() * sizeof(pal_device_id_t));
memset(deviceIdConfigs, 0, new_devices.size() * sizeof(struct pal_device));
mPalInDeviceIds = deviceId;
mPalInDevice = deviceIdConfigs;
}
noPalDevices = getPalDeviceIds(new_devices, mPalInDeviceIds);
AHAL_DBG("noPalDevices: %d , new_devices: %zu",
noPalDevices, new_devices.size());
if (noPalDevices != new_devices.size() ||
noPalDevices >= PAL_DEVICE_IN_MAX) {
AHAL_ERR("Device count mismatch! Expected: %d Got: %zu", noPalDevices, new_devices.size());
ret = -EINVAL;
goto done;
}
device_cap_query = (pal_param_device_capability_t *)
malloc(sizeof(pal_param_device_capability_t));
if (!device_cap_query) {
AHAL_ERR("Failed to allocate device_cap_query!");
ret = -ENOMEM;
goto done;
}
for (int i = 0; i < noPalDevices; i++) {
mPalInDevice[i].id = mPalInDeviceIds[i];
if (((mPalInDeviceIds[i] == PAL_DEVICE_IN_USB_DEVICE) ||
(mPalInDeviceIds[i] == PAL_DEVICE_IN_USB_HEADSET)) && device_cap_query) {
mPalInDevice[i].address.card_id = adevice->usb_card_id_;
mPalInDevice[i].address.device_num = adevice->usb_dev_num_;
device_cap_query->id = mPalInDeviceIds[i];
device_cap_query->addr.card_id = adevice->usb_card_id_;
device_cap_query->addr.device_num = adevice->usb_dev_num_;
device_cap_query->config = &dynamic_media_config;
device_cap_query->is_playback = true;
ret = pal_get_param(PAL_PARAM_ID_DEVICE_CAPABILITY,(void **)&device_cap_query,
&payload_size, nullptr);
if (ret<0) {
AHAL_ERR("Error usb device is not connected");
ret = -ENOSYS;
goto done;
}
}
mPalInDevice[i].config.sample_rate = mPalInDevice[0].config.sample_rate;
mPalInDevice[i].config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
mPalInDevice[i].config.ch_info = ch_info;
mPalInDevice[i].config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE;
if ((mPalInDeviceIds[i] == PAL_DEVICE_IN_USB_DEVICE) ||
(mPalInDeviceIds[i] == PAL_DEVICE_IN_USB_HEADSET)) {
mPalInDevice[i].address.card_id = adevice->usb_card_id_;
mPalInDevice[i].address.device_num = adevice->usb_dev_num_;
}
strlcpy(mPalInDevice[i].custom_config.custom_key, "",
sizeof(mPalInDevice[i].custom_config.custom_key));
/* HDR use case check */
if (is_hdr_mode_enabled())
setup_hdr_usecase(&mPalInDevice[i]);
if (source_ == AUDIO_SOURCE_CAMCORDER && adevice->cameraOrientation == CAMERA_DEFAULT) {
strlcat(mPalInDevice[i].custom_config.custom_key, "camcorder_landscape;",
sizeof(mPalInDevice[i].custom_config.custom_key));
AHAL_INFO("Setting custom key as %s", mPalInDevice[i].custom_config.custom_key);
}
#ifdef DYNAMIC_SR_ENABLED
if (((usecase_ == USECASE_AUDIO_RECORD_VOIP) ||
(usecase_ == USECASE_AUDIO_RECORD)) &&
((mPalInDevice[i].id == PAL_DEVICE_IN_HANDSET_MIC) ||
(mPalInDevice[i].id == PAL_DEVICE_IN_SPEAKER_MIC) ||
(mPalInDevice[i].id == PAL_DEVICE_IN_WIRED_HEADSET))) {
if (config_.sample_rate == 8000) {
strlcat(mPalInDevice[i].custom_config.custom_key, "8K;",
sizeof(mPalInDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 16000) {
strlcat(mPalInDevice[i].custom_config.custom_key, "16K;",
sizeof(mPalInDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 32000) {
strlcat(mPalInDevice[i].custom_config.custom_key, "32K;",
sizeof(mPalInDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 48000) {
strlcat(mPalInDevice[i].custom_config.custom_key, "48K;",
sizeof(mPalInDevice[i].custom_config.custom_key));
} else {
AHAL_DBG("No custom config to set for usecase %d for sr %d",
usecase_, config_.sample_rate);
}
AHAL_DBG("setting SR for usecase %d as %d", usecase_, config_.sample_rate);
}
#endif
}
mAndroidInDevices = new_devices;
if (pal_stream_handle_)
ret = pal_stream_set_device(pal_stream_handle_, noPalDevices, mPalInDevice);
}
done:
if (device_cap_query) {
free(device_cap_query);
device_cap_query = NULL;
}
stream_mutex_.unlock();
AHAL_DBG("exit %d", ret);
return ret;
}
bool StreamInPrimary::getParameters(struct str_parms *query,
struct str_parms *reply) {
bool found = false;
char value[256];
if (usecase_ == USECASE_AUDIO_RECORD_COMPRESS) {
if (config_.format == AUDIO_FORMAT_AAC_LC ||
config_.format == AUDIO_FORMAT_AAC_ADTS_LC ||
config_.format == AUDIO_FORMAT_AAC_ADTS_HE_V1 ||
config_.format == AUDIO_FORMAT_AAC_ADTS_HE_V2) {
// query for AAC bitrate
if (str_parms_get_str(query,
CompressCapture::kAudioParameterDSPAacBitRate,
value, sizeof(value)) >= 0) {
value[0] = '\0';
// fill in the AAC bitrate
if (mIsBitRateSet &&
(str_parms_add_int(
reply, CompressCapture::kAudioParameterDSPAacBitRate,
mCompressStreamAdjBitRate) >= 0)) {
mIsBitRateGet = found = true;
}
}
}
}
return found;
}
int StreamInPrimary::SetParameters(const char* kvpairs) {
struct str_parms *parms = (str_parms *)NULL;
int ret = 0;
AHAL_DBG("enter: kvpairs: %s", kvpairs);
if(!mInitialized)
goto exit;
parms = str_parms_create_str(kvpairs);
if (!parms)
goto exit;
if (usecase_ == USECASE_AUDIO_RECORD_COMPRESS) {
if (CompressCapture::parseMetadata(parms, &config_,
mCompressStreamAdjBitRate)) {
mIsBitRateSet = true;
}
}
str_parms_destroy(parms);
exit:
AHAL_DBG("exit %d", ret);
return ret;
}
int StreamInPrimary::Open() {
int ret = 0;
uint8_t channels = 0;
struct pal_channel_info ch_info = {0, {0}};
uint32_t inBufSize = 0;
uint32_t inBufCount = NO_OF_BUF;
struct pal_buffer_config inBufCfg = {0, 0, 0};
void *handle = nullptr;
pal_param_device_capability_t *device_cap_query = NULL;
size_t payload_size = 0;
dynamic_media_config_t dynamic_media_config;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
AHAL_INFO("Enter: InPrimary usecase(%d: %s)", GetUseCase(), use_case_table[GetUseCase()]);
if (!mInitialized) {
AHAL_ERR("Not initialized, returning error");
ret = -EINVAL;
goto exit;
}
handle = audio_extn_sound_trigger_check_and_get_session(this);
if (handle) {
AHAL_VERBOSE("Found existing pal stream handle associated with capture handle");
pal_stream_handle_ = (pal_stream_handle_t *)handle;
goto set_buff_size;
}
channels = audio_channel_count_from_in_mask(config_.channel_mask);
if (channels == 0) {
AHAL_ERR("invalid channel count");
ret = -EINVAL;
goto exit;
}
//need to convert channel mask to pal channel mask
if (channels == 8) {
ch_info.channels = 8;
ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
ch_info.ch_map[2] = PAL_CHMAP_CHANNEL_C;
ch_info.ch_map[3] = PAL_CHMAP_CHANNEL_LFE;
ch_info.ch_map[4] = PAL_CHMAP_CHANNEL_LB;
ch_info.ch_map[5] = PAL_CHMAP_CHANNEL_RB;
ch_info.ch_map[6] = PAL_CHMAP_CHANNEL_LS;
ch_info.ch_map[6] = PAL_CHMAP_CHANNEL_RS;
} else if (channels == 7) {
ch_info.channels = 7;
ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
ch_info.ch_map[2] = PAL_CHMAP_CHANNEL_C;
ch_info.ch_map[3] = PAL_CHMAP_CHANNEL_LFE;
ch_info.ch_map[4] = PAL_CHMAP_CHANNEL_LB;
ch_info.ch_map[5] = PAL_CHMAP_CHANNEL_RB;
ch_info.ch_map[6] = PAL_CHMAP_CHANNEL_LS;
} else if (channels == 6) {
ch_info.channels = 6;
ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
ch_info.ch_map[2] = PAL_CHMAP_CHANNEL_C;
ch_info.ch_map[3] = PAL_CHMAP_CHANNEL_LFE;
ch_info.ch_map[4] = PAL_CHMAP_CHANNEL_LB;
ch_info.ch_map[5] = PAL_CHMAP_CHANNEL_RB;
} else if (channels == 5) {
ch_info.channels = 5;
ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
ch_info.ch_map[2] = PAL_CHMAP_CHANNEL_C;
ch_info.ch_map[3] = PAL_CHMAP_CHANNEL_LFE;
ch_info.ch_map[4] = PAL_CHMAP_CHANNEL_RC;
} else if (channels == 4) {
ch_info.channels = 4;
ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
ch_info.ch_map[2] = PAL_CHMAP_CHANNEL_C;
ch_info.ch_map[3] = PAL_CHMAP_CHANNEL_LFE;
} else if (channels == 3) {
ch_info.channels = 3;
ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
ch_info.ch_map[2] = PAL_CHMAP_CHANNEL_C;
} else if (channels == 2) {
ch_info.channels = 2;
ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
ch_info.ch_map[1] = PAL_CHMAP_CHANNEL_FR;
} else {
ch_info.channels = 1;
ch_info.ch_map[0] = PAL_CHMAP_CHANNEL_FL;
}
streamAttributes_.type = StreamInPrimary::GetPalStreamType(flags_,
config_.sample_rate);
if (source_ == AUDIO_SOURCE_VOICE_UPLINK) {
streamAttributes_.type = PAL_STREAM_VOICE_CALL_RECORD;
streamAttributes_.info.voice_rec_info.record_direction = INCALL_RECORD_VOICE_UPLINK;
} else if (source_ == AUDIO_SOURCE_VOICE_DOWNLINK) {
streamAttributes_.type = PAL_STREAM_VOICE_CALL_RECORD;
streamAttributes_.info.voice_rec_info.record_direction = INCALL_RECORD_VOICE_DOWNLINK;
} else if (source_ == AUDIO_SOURCE_VOICE_CALL) {
streamAttributes_.type = PAL_STREAM_VOICE_CALL_RECORD;
streamAttributes_.info.voice_rec_info.record_direction = INCALL_RECORD_VOICE_UPLINK_DOWNLINK;
}
streamAttributes_.flags = (pal_stream_flags_t)0;
streamAttributes_.direction = PAL_AUDIO_INPUT;
streamAttributes_.in_media_config.sample_rate = config_.sample_rate;
if (is_pcm_format(config_.format)) {
streamAttributes_.in_media_config.aud_fmt_id = getFormatId.at(config_.format);
streamAttributes_.in_media_config.bit_width = format_to_bitwidth_table[config_.format];
} else if (!is_pcm_format(config_.format) && usecase_ == USECASE_AUDIO_RECORD_COMPRESS) {
streamAttributes_.in_media_config.aud_fmt_id = getFormatId.at(config_.format);
streamAttributes_.in_media_config.bit_width = compressRecordBitWidthTable.at(config_.format);
} else {
/*TODO:Update this to support compressed capture using hal apis*/
streamAttributes_.in_media_config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
streamAttributes_.in_media_config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE;
}
streamAttributes_.in_media_config.ch_info = ch_info;
if (streamAttributes_.type == PAL_STREAM_ULTRA_LOW_LATENCY) {
if (usecase_ == USECASE_AUDIO_RECORD_MMAP)
streamAttributes_.flags = (pal_stream_flags_t)
(PAL_STREAM_FLAG_MMAP_NO_IRQ);
else if (usecase_ == USECASE_AUDIO_RECORD_LOW_LATENCY)
streamAttributes_.flags = (pal_stream_flags_t)
(PAL_STREAM_FLAG_MMAP);
}
if (streamAttributes_.type == PAL_STREAM_PROXY) {
if (isDeviceAvailable(PAL_DEVICE_IN_PROXY))
streamAttributes_.info.opt_stream_info.tx_proxy_type = PAL_STREAM_PROXY_TX_WFD;
else if (isDeviceAvailable(PAL_DEVICE_IN_TELEPHONY_RX))
streamAttributes_.info.opt_stream_info.tx_proxy_type = PAL_STREAM_PROXY_TX_TELEPHONY_RX;
}
device_cap_query = (pal_param_device_capability_t *)malloc(sizeof(pal_param_device_capability_t));
if ((mPalInDevice->id == PAL_DEVICE_IN_USB_DEVICE || mPalInDevice->id ==
PAL_DEVICE_IN_USB_HEADSET) && device_cap_query && adevice) {
device_cap_query->id = mPalInDevice->id;
device_cap_query->addr.card_id = adevice->usb_card_id_;
device_cap_query->addr.device_num = adevice->usb_dev_num_;
device_cap_query->config = &dynamic_media_config;
device_cap_query->is_playback = true;
ret = pal_get_param(PAL_PARAM_ID_DEVICE_CAPABILITY,(void **)&device_cap_query,
&payload_size, nullptr);
if (ret<0) {
AHAL_DBG("Error usb device is not connected");
ret = -ENOSYS;
goto exit;
}
}
AHAL_DBG("(%x:ret)", ret);
ret = pal_stream_open(&streamAttributes_,
mAndroidInDevices.size(),
mPalInDevice,
0,
NULL,
&pal_callback,
(uint64_t)this,
&pal_stream_handle_);
if (ret) {
AHAL_ERR("Pal Stream Open Error (%x)", ret);
ret = -EINVAL;
goto exit;
}
// TODO configure this for any audio format
//PAL input compressed stream is used only for compress capture
if (streamAttributes_.type == PAL_STREAM_COMPRESSED) {
pal_param_payload *param_payload = nullptr;
param_payload = (pal_param_payload *)calloc(
1, sizeof(pal_param_payload) + sizeof(pal_snd_enc_t));
if (!param_payload) {
AHAL_ERR("calloc failed for size %zu",
sizeof(pal_param_payload) + sizeof(pal_snd_enc_t));
} else {
/**
* encoder mode
0x2 AAC_AOT_LC
0x5 AAC_AOT_SBR
0x1d AAC_AOT_PS
* format flag
0x0 AAC_FORMAT_FLAG_ADTS
0x1 AAC_FORMAT_FLAG_LOAS
0x3 AAC_FORMAT_FLAG_RAW
0x4 AAC_FORMAT_FLAG_LATM
**/
param_payload->payload_size = sizeof(pal_snd_enc_t);
if (config_.format == AUDIO_FORMAT_AAC_LC ||
config_.format == AUDIO_FORMAT_AAC_ADTS_LC) {
palSndEnc.aac_enc.enc_cfg.aac_enc_mode = 0x2;
palSndEnc.aac_enc.enc_cfg.aac_fmt_flag = 0x00;
} else if (config_.format == AUDIO_FORMAT_AAC_ADTS_HE_V1) {
palSndEnc.aac_enc.enc_cfg.aac_enc_mode = 0x5;
palSndEnc.aac_enc.enc_cfg.aac_fmt_flag = 0x00;
} else if (config_.format == AUDIO_FORMAT_AAC_ADTS_HE_V2) {
palSndEnc.aac_enc.enc_cfg.aac_enc_mode = 0x1d;
palSndEnc.aac_enc.enc_cfg.aac_fmt_flag = 0x00;
} else {
palSndEnc.aac_enc.enc_cfg.aac_enc_mode = 0x2;
palSndEnc.aac_enc.enc_cfg.aac_fmt_flag = 0x00;
}
if (mIsBitRateSet && mIsBitRateGet) {
palSndEnc.aac_enc.aac_bit_rate = mCompressStreamAdjBitRate;
mIsBitRateSet = mIsBitRateGet = false;
AHAL_DBG("compress aac bitrate configured: %d",
palSndEnc.aac_enc.aac_bit_rate);
} else {
palSndEnc.aac_enc.aac_bit_rate =
CompressCapture::sSampleRateToDefaultBitRate.at(
config_.sample_rate);
}
memcpy(param_payload->payload, &palSndEnc,
param_payload->payload_size);
ret = pal_stream_set_param(pal_stream_handle_,
PAL_PARAM_ID_CODEC_CONFIGURATION,
param_payload);
if (ret) AHAL_ERR("Pal Set Param Error (%x)", ret);
free(param_payload);
}
}
set_buff_size:
if (usecase_ == USECASE_AUDIO_RECORD_MMAP) {
inBufSize = MMAP_PERIOD_SIZE * audio_bytes_per_frame(
audio_channel_count_from_in_mask(config_.channel_mask),
config_.format);
inBufCount = MMAP_PERIOD_COUNT_DEFAULT;
} else if (usecase_ == USECASE_AUDIO_RECORD_LOW_LATENCY) {
inBufSize = ULL_PERIOD_SIZE * audio_bytes_per_frame(
audio_channel_count_from_in_mask(config_.channel_mask),
config_.format);
inBufCount = ULL_PERIOD_COUNT_DEFAULT;
} else
inBufSize = StreamInPrimary::GetBufferSize();
if (usecase_ == USECASE_AUDIO_RECORD_VOIP)
inBufCount = VOIP_PERIOD_COUNT_DEFAULT;
if (!handle) {
inBufCfg.buf_size = inBufSize;
inBufCfg.buf_count = inBufCount;
ret = pal_stream_set_buffer_size(pal_stream_handle_, &inBufCfg, NULL);
inBufSize = inBufCfg.buf_size;
if (ret) {
AHAL_ERR("Pal Stream set buffer size Error (%x)", ret);
}
}
fragments_ = inBufCount;
fragment_size_ = inBufSize;
exit:
if (device_cap_query) {
free(device_cap_query);
device_cap_query = NULL;
}
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
uint32_t StreamInPrimary::GetBufferSizeForLowLatencyRecord() {
int trial = 0;
char value[PROPERTY_VALUE_MAX] = {0};
int configured_low_latency_record_multiplier = ULL_PERIOD_MULTIPLIER;
if (property_get("vendor.audio.ull_record_period_multiplier", value, NULL) > 0) {
trial = atoi(value);
if(trial < ULL_PERIOD_MULTIPLIER && trial > 0)
configured_low_latency_record_multiplier = trial;
}
return ULL_PERIOD_SIZE * configured_low_latency_record_multiplier *
audio_bytes_per_frame(
audio_channel_count_from_in_mask(config_.channel_mask),
config_.format);
}
/* in bytes */
uint32_t StreamInPrimary::GetBufferSize() {
struct pal_stream_attributes streamAttributes_;
#ifdef EC_REF_CAPTURE_ENABLED
bool isEchoRef = false;
#endif
size_t size = 0;
uint32_t bytes_per_period_sample = 0;
streamAttributes_.type = StreamInPrimary::GetPalStreamType(flags_,
config_.sample_rate);
#ifdef EC_REF_CAPTURE_ENABLED
if (streamAttributes_.type == PAL_STREAM_RAW && isDeviceAvailable(PAL_DEVICE_IN_ECHO_REF))
isEchoRef = true;
#endif
if (streamAttributes_.type == PAL_STREAM_VOIP_TX) {
size = (DEFAULT_VOIP_BUF_DURATION_MS * config_.sample_rate / 1000) *
audio_bytes_per_frame(
audio_channel_count_from_in_mask(config_.channel_mask),
config_.format);
} else if (streamAttributes_.type == PAL_STREAM_LOW_LATENCY) {
size = LOW_LATENCY_CAPTURE_PERIOD_SIZE *
audio_bytes_per_frame(
audio_channel_count_from_in_mask(config_.channel_mask),
config_.format);
} else if (streamAttributes_.type == PAL_STREAM_ULTRA_LOW_LATENCY) {
return GetBufferSizeForLowLatencyRecord();
} else if (streamAttributes_.type == PAL_STREAM_VOICE_CALL_RECORD) {
return (config_.sample_rate * AUDIO_CAPTURE_PERIOD_DURATION_MSEC/ 1000) *
audio_bytes_per_frame(
audio_channel_count_from_in_mask(config_.channel_mask),
config_.format);
} else if (streamAttributes_.type == PAL_STREAM_PROXY) {
if (isDeviceAvailable(PAL_DEVICE_IN_TELEPHONY_RX)) {
audio_stream_in* stream_in;
GetStreamHandle(&stream_in);
return AFE_PROXY_RECORD_PERIOD_SIZE * audio_stream_in_frame_size(stream_in);
} else
return config_.frame_count *
audio_bytes_per_frame(
audio_channel_count_from_in_mask(config_.channel_mask),
config_.format);
} else if (streamAttributes_.type == PAL_STREAM_COMPRESSED) {
// TODO make this allocation with respect to AUDIO_FORMAT
return COMPRESS_CAPTURE_AAC_MAX_OUTPUT_BUFFER_SIZE;
} else {
/* this else condition will be other stream types like deepbuffer/RAW.. */
size = (config_.sample_rate * AUDIO_CAPTURE_PERIOD_DURATION_MSEC) /1000;
size *= audio_bytes_per_frame(
audio_channel_count_from_in_mask(config_.channel_mask),
config_.format);;
}
/* make sure the size is multiple of 32 bytes and additionally multiple of
* the frame_size (required for 24bit samples and non-power-of-2 channel counts)
* At 48 kHz mono 16-bit PCM:
* 5.000 ms = 240 frames = 15*16*1*2 = 480, a whole multiple of 32 (15)
* 3.333 ms = 160 frames = 10*16*1*2 = 320, a whole multiple of 32 (10)
* Also, make sure the size is multiple of bytes per period sample
*/
bytes_per_period_sample = audio_bytes_per_sample(config_.format) *
audio_channel_count_from_in_mask(config_.channel_mask);
size = nearest_multiple(size, lcm(32, bytes_per_period_sample));
return size;
}
int StreamInPrimary::GetInputUseCase(audio_input_flags_t halStreamFlags, audio_source_t source)
{
// TODO: cover other usecases
int usecase = USECASE_AUDIO_RECORD;
if (config_.sample_rate == LOW_LATENCY_CAPTURE_SAMPLE_RATE &&
(halStreamFlags & AUDIO_INPUT_FLAG_TIMESTAMP) == 0 &&
(halStreamFlags & AUDIO_INPUT_FLAG_COMPRESS) == 0 &&
(halStreamFlags & AUDIO_INPUT_FLAG_FAST) != 0 &&
(!(isDeviceAvailable(PAL_DEVICE_IN_PROXY))))
usecase = USECASE_AUDIO_RECORD_LOW_LATENCY;
if ((halStreamFlags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0)
usecase = USECASE_AUDIO_RECORD_MMAP;
else if (source == AUDIO_SOURCE_VOICE_COMMUNICATION &&
halStreamFlags & AUDIO_INPUT_FLAG_VOIP_TX)
usecase = USECASE_AUDIO_RECORD_VOIP;
else if ((halStreamFlags & AUDIO_INPUT_FLAG_DIRECT) != 0)
usecase = USECASE_AUDIO_RECORD_COMPRESS;
return usecase;
}
int StreamInPrimary::SetMicMute(bool mute) {
int ret = 0;
AHAL_DBG("Enter mute %d for input session", mute);
stream_mutex_.lock();
if (pal_stream_handle_) {
AHAL_DBG("Enter if mute %d for input session", mute);
ret = pal_stream_set_mute(pal_stream_handle_, mute);
if (ret)
AHAL_ERR("Error applying mute %d for input session", mute);
}
stream_mutex_.unlock();
AHAL_DBG("Exit");
return ret;
}
ssize_t StreamInPrimary::onReadError(size_t bytes, size_t ret) {
// standby streams upon read failures and sleep for buffer duration.
AHAL_ERR("read failed %d usecase(%d: %s)", ret, GetUseCase(), use_case_table[GetUseCase()]);
Standby();
uint32_t byteWidth = streamAttributes_.in_media_config.bit_width / 8;
uint32_t sampleRate = streamAttributes_.in_media_config.sample_rate;
uint32_t channelCount = streamAttributes_.in_media_config.ch_info.channels;
uint32_t frameSize = byteWidth * channelCount;
if (frameSize == 0 || sampleRate == 0) {
AHAL_ERR("invalid frameSize=%d, sampleRate=%d", frameSize, sampleRate);
return -EINVAL;
} else {
usleep((uint64_t)bytes * 1000000 / frameSize / sampleRate);
}
return bytes;
}
ssize_t StreamInPrimary::read(const void *buffer, size_t bytes) {
ssize_t ret = 0;
int retry_count = MAX_READ_RETRY_COUNT;
ssize_t size = 0;
struct pal_buffer palBuffer;
palBuffer.buffer = (uint8_t *)buffer;
palBuffer.size = bytes;
palBuffer.offset = 0;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
AHAL_VERBOSE("requested bytes: %zu", bytes);
stream_mutex_.lock();
if (!pal_stream_handle_) {
AutoPerfLock perfLock;
ret = Open();
if (ret < 0)
goto exit;
}
if (is_st_session) {
ATRACE_BEGIN("hal: lab read");
memset(palBuffer.buffer, 0, palBuffer.size);
if (!audio_extn_sound_trigger_check_session_activity(this)) {
AHAL_DBG("sound trigger session not available");
ATRACE_END();
goto exit;
}
if (!stream_started_) {
adevice->num_va_sessions_++;
stream_started_ = true;
}
while (retry_count--) {
ret = pal_stream_read(pal_stream_handle_, &palBuffer);
if (ret < 0) {
memset(palBuffer.buffer, 0, palBuffer.size);
AHAL_ERR("error, failed to read data from PAL");
ATRACE_END();
ret = bytes;
goto exit;
} else {
size += ret;
if (ret < palBuffer.size) {
palBuffer.buffer += ret;
palBuffer.size -= ret;
} else {
break;
}
}
}
ATRACE_END();
goto exit;
}
if (!stream_started_) {
AutoPerfLock perfLock;
ret = pal_stream_start(pal_stream_handle_);
if (ret) {
AHAL_ERR("failed to start stream. ret=%d", ret);
pal_stream_close(pal_stream_handle_);
pal_stream_handle_ = NULL;
goto exit;
}
stream_started_ = true;
/* set cached volume if any, dont return failure back up */
if (volume_) {
ret = pal_stream_set_volume(pal_stream_handle_, volume_);
if (ret) {
AHAL_ERR("Pal Stream volume Error (%x)", ret);
}
}
/*apply cached mic mute*/
if (adevice->mute_) {
pal_stream_set_mute(pal_stream_handle_, adevice->mute_);
}
}
if (!effects_applied_) {
if (isECEnabled && isNSEnabled) {
ret = pal_add_remove_effect(pal_stream_handle_,PAL_AUDIO_EFFECT_ECNS,true);
} else if (isECEnabled) {
ret = pal_add_remove_effect(pal_stream_handle_,PAL_AUDIO_EFFECT_EC,true);
} else if (isNSEnabled) {
ret = pal_add_remove_effect(pal_stream_handle_,PAL_AUDIO_EFFECT_NS,true);
} else {
ret = pal_add_remove_effect(pal_stream_handle_,PAL_AUDIO_EFFECT_ECNS,false);
}
effects_applied_ = true;
}
ret = pal_stream_read(pal_stream_handle_, &palBuffer);
AHAL_VERBOSE("received size= %d",palBuffer.size);
if (usecase_ == USECASE_AUDIO_RECORD_COMPRESS && ret > 0) {
size = palBuffer.size;
mCompressReadCalls++;
}
// mute pcm data if sva client is reading lab data
if (adevice->num_va_sessions_ > 0 &&
source_ != AUDIO_SOURCE_VOICE_RECOGNITION &&
property_get_bool("persist.vendor.audio.va_concurrency_mute_enabled",
false)) {
memset(palBuffer.buffer, 0, palBuffer.size);
}
exit:
if (mBytesRead <= UINT64_MAX - bytes) {
mBytesRead += bytes;
} else {
mBytesRead = UINT64_MAX;
}
stream_mutex_.unlock();
clock_gettime(CLOCK_MONOTONIC, &readAt);
AHAL_DBG("Exit: returning size: %zu size ", size);
return (ret < 0 ? onReadError(bytes, ret) : (size > 0 ? size : bytes));
}
int StreamInPrimary::FillHalFnPtrs() {
int ret = 0;
stream_.get()->common.get_sample_rate = astream_in_get_sample_rate;
stream_.get()->common.set_sample_rate = astream_set_sample_rate;
stream_.get()->common.get_buffer_size = astream_in_get_buffer_size;
stream_.get()->common.get_channels = astream_in_get_channels;
stream_.get()->common.get_format = astream_in_get_format;
stream_.get()->common.set_format = astream_set_format;
stream_.get()->common.standby = astream_in_standby;
stream_.get()->common.dump = astream_dump;
stream_.get()->common.set_parameters = astream_in_set_parameters;
stream_.get()->common.get_parameters = astream_in_get_parameters;
stream_.get()->common.add_audio_effect = astream_in_add_audio_effect;
stream_.get()->common.remove_audio_effect = astream_in_remove_audio_effect;
stream_.get()->set_gain = astream_in_set_gain;
stream_.get()->read = in_read;
stream_.get()->get_input_frames_lost = astream_in_get_input_frames_lost;
stream_.get()->get_capture_position = astream_in_get_capture_position;
stream_.get()->get_active_microphones = astream_in_get_active_microphones;
stream_.get()->set_microphone_direction =
astream_in_set_microphone_direction;
stream_.get()->set_microphone_field_dimension =
in_set_microphone_field_dimension;
stream_.get()->update_sink_metadata_v7 = in_update_sink_metadata_v7;
return ret;
}
StreamInPrimary::StreamInPrimary(audio_io_handle_t handle,
const std::set<audio_devices_t> &devices,
audio_input_flags_t flags,
struct audio_config *config,
const char *address __unused,
audio_source_t source) :
StreamPrimary(handle, devices, config),
mAndroidInDevices(devices),
flags_(flags)
{
stream_ = std::shared_ptr<audio_stream_in> (new audio_stream_in());
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
pal_stream_handle_ = NULL;
mInitialized = false;
int noPalDevices = 0;
int ret = 0;
readAt.tv_sec = 0;
readAt.tv_nsec = 0;
void *st_handle = nullptr;
pal_param_payload *payload = nullptr;
AHAL_DBG("enter: handle (%x) format(%#x) sample_rate(%d) channel_mask(%#x) devices(%zu) flags(%#x)"\
, handle, config->format, config->sample_rate, config->channel_mask,
mAndroidInDevices.size(), flags);
if (!(stream_.get())) {
AHAL_ERR("stream_ new allocation failed");
goto error;
}
if (AudioExtn::audio_devices_cmp(mAndroidInDevices, audio_is_usb_in_device)) {
// get capability from device of USB
device_cap_query_ = (pal_param_device_capability_t *)
calloc(1, sizeof(pal_param_device_capability_t));
if (!device_cap_query_) {
AHAL_ERR("Failed to allocate mem for device_cap_query_");
goto error;
}
dynamic_media_config_t *dynamic_media_config = (dynamic_media_config_t *)
calloc(1, sizeof(dynamic_media_config_t));
if (!dynamic_media_config) {
free(device_cap_query_);
AHAL_ERR("Failed to allocate mem for dynamic_media_config");
goto error;
}
size_t payload_size = 0;
device_cap_query_->id = PAL_DEVICE_IN_USB_HEADSET;
device_cap_query_->addr.card_id = adevice->usb_card_id_;
device_cap_query_->addr.device_num = adevice->usb_dev_num_;
device_cap_query_->config = dynamic_media_config;
device_cap_query_->is_playback = false;
ret = pal_get_param(PAL_PARAM_ID_DEVICE_CAPABILITY,
(void **)&device_cap_query_,
&payload_size, nullptr);
if (ret < 0) {
AHAL_ERR("Error usb device is not connected");
free(dynamic_media_config);
free(device_cap_query_);
dynamic_media_config = NULL;
device_cap_query_ = NULL;
}
if (dynamic_media_config) {
AHAL_DBG("usb fs=%d format=%d mask=%x",
dynamic_media_config->sample_rate[0],
dynamic_media_config->format[0], dynamic_media_config->mask[0]);
if (!config->sample_rate) {
config->sample_rate = dynamic_media_config->sample_rate[0];
config->channel_mask = (audio_channel_mask_t) dynamic_media_config->mask[0];
config->format = (audio_format_t)dynamic_media_config->format[0];
memcpy(&config_, config, sizeof(struct audio_config));
}
}
}
/* this is required for USB otherwise adev_open_input_stream is failed */
if (!config_.sample_rate)
config_.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
if (!config_.channel_mask)
config_.channel_mask = AUDIO_CHANNEL_IN_MONO;
if (!config_.format)
config_.format = AUDIO_FORMAT_PCM_16_BIT;
/*
* Audio config set from client may not be same as config used in pal,
* update audio config here so that AudioFlinger can acquire correct
* config used in pal/hal and configure record buffer converter properly.
*/
st_handle = audio_extn_sound_trigger_check_and_get_session(this);
if (st_handle) {
AHAL_VERBOSE("Found existing pal stream handle associated with capture handle");
pal_stream_handle_ = (pal_stream_handle_t *)st_handle;
payload = (pal_param_payload *)calloc(1,
sizeof(pal_param_payload) + sizeof(struct pal_stream_attributes));
if (!payload) {
AHAL_ERR("Failed to allocate memory for stream attributes");
goto error;
}
payload->payload_size = sizeof(struct pal_stream_attributes);
ret = pal_stream_get_param(pal_stream_handle_, PAL_PARAM_ID_STREAM_ATTRIBUTES, &payload);
if (ret) {
AHAL_ERR("Failed to get pal stream attributes, ret = %d", ret);
if (payload)
free(payload);
goto error;
}
memcpy(&streamAttributes_, payload->payload, payload->payload_size);
if (streamAttributes_.in_media_config.ch_info.channels == 1)
config_.channel_mask = AUDIO_CHANNEL_IN_MONO;
else if (streamAttributes_.in_media_config.ch_info.channels == 2)
config_.channel_mask = AUDIO_CHANNEL_IN_STEREO;
config_.format = AUDIO_FORMAT_PCM_16_BIT;
config_.sample_rate = streamAttributes_.in_media_config.sample_rate;
/*
* reset pal_stream_handle in case standby come before
* read as anyway it will be updated in StreamInPrimary::Open
*/
if (payload)
free(payload);
pal_stream_handle_ = nullptr;
}
AHAL_DBG("local : handle (%x) format(%#x) sample_rate(%d) channel_mask(%#x) devices(%#x) flags(%#x)"\
, handle, config_.format, config_.sample_rate, config_.channel_mask,
AudioExtn::get_device_types(devices), flags);
source_ = source;
mAndroidInDevices = devices;
if(mAndroidInDevices.empty())
mAndroidInDevices.insert(AUDIO_DEVICE_IN_DEFAULT);
AHAL_DBG("No of devices %zu", mAndroidInDevices.size());
mPalInDeviceIds = (pal_device_id_t*) calloc(mAndroidInDevices.size(), sizeof(pal_device_id_t));
if (!mPalInDeviceIds) {
goto error;
}
noPalDevices = getPalDeviceIds(devices, mPalInDeviceIds);
if (noPalDevices != mAndroidInDevices.size()) {
AHAL_ERR("mismatched pal %d and hal devices %zu", noPalDevices, mAndroidInDevices.size());
goto error;
}
mPalInDevice = (struct pal_device*) calloc(mAndroidInDevices.size(), sizeof(struct pal_device));
if (!mPalInDevice) {
goto error;
}
for (int i = 0; i < mAndroidInDevices.size(); i++) {
mPalInDevice[i].id = mPalInDeviceIds[i];
mPalInDevice[i].config.sample_rate = config->sample_rate;
mPalInDevice[i].config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
// ch_info memory is allocated at resource manager:getdeviceconfig
mPalInDevice[i].config.ch_info = {0, {0}};
mPalInDevice[i].config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE; // TODO: need to convert this from output format
if ((mPalInDeviceIds[i] == PAL_DEVICE_IN_USB_DEVICE) ||
(mPalInDeviceIds[i] == PAL_DEVICE_IN_USB_HEADSET)) {
mPalInDevice[i].address.card_id = adevice->usb_card_id_;
mPalInDevice[i].address.device_num = adevice->usb_dev_num_;
}
/* HDR use case check */
if ((source_ == AUDIO_SOURCE_UNPROCESSED) &&
(config_.sample_rate == 48000)) {
uint8_t channels =
audio_channel_count_from_in_mask(config_.channel_mask);
if (channels == 4) {
if (is_hdr_mode_enabled()) {
flags = flags_ = AUDIO_INPUT_FLAG_RAW;
setup_hdr_usecase(&mPalInDevice[i]);
}
}
}
}
usecase_ = GetInputUseCase(flags, source);
for (int i = 0; i < mAndroidInDevices.size(); i++) {
memset(mPalInDevice[i].custom_config.custom_key, 0,
sizeof(mPalInDevice[i].custom_config.custom_key));
strlcpy(mPalInDevice[i].custom_config.custom_key, "",
sizeof(mPalInDevice[i].custom_config.custom_key));
if (source_ == AUDIO_SOURCE_CAMCORDER && adevice->cameraOrientation == CAMERA_DEFAULT) {
strlcat(mPalInDevice[i].custom_config.custom_key, "camcorder_landscape;",
sizeof(mPalInDevice[i].custom_config.custom_key));
AHAL_INFO("Setting custom key as %s", mPalInDevice[i].custom_config.custom_key);
}
#ifdef DYNAMIC_SR_ENABLED
if (((usecase_ == USECASE_AUDIO_RECORD_VOIP) ||
(usecase_ == USECASE_AUDIO_RECORD)) &&
((mPalInDevice[i].id == PAL_DEVICE_IN_HANDSET_MIC) ||
(mPalInDevice[i].id == PAL_DEVICE_IN_SPEAKER_MIC) ||
(mPalInDevice[i].id == PAL_DEVICE_IN_WIRED_HEADSET))) {
if (config_.sample_rate == 8000) {
strlcat(mPalInDevice[i].custom_config.custom_key, "8K;",
sizeof(mPalInDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 16000) {
strlcat(mPalInDevice[i].custom_config.custom_key, "16K;",
sizeof(mPalInDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 32000) {
strlcat(mPalInDevice[i].custom_config.custom_key, "32K;",
sizeof(mPalInDevice[i].custom_config.custom_key));
} else if (config_.sample_rate == 48000) {
strlcat(mPalInDevice[i].custom_config.custom_key, "48K;",
sizeof(mPalInDevice[i].custom_config.custom_key));
} else {
AHAL_DBG("No custom config to set for usecase %d for sr %d",
usecase_, config_.sample_rate);
}
AHAL_DBG("setting SR for usecase %d as %d", usecase_, config_.sample_rate);
}
#endif
}
if (flags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) {
stream_.get()->start = astream_in_mmap_noirq_start;
stream_.get()->stop = astream_in_mmap_noirq_stop;
stream_.get()->create_mmap_buffer = astream_in_create_mmap_buffer;
stream_.get()->get_mmap_position = astream_in_get_mmap_position;
}
mInitialized = true;
error:
(void)FillHalFnPtrs();
AHAL_DBG("Exit");
return;
}
StreamInPrimary::~StreamInPrimary() {
stream_mutex_.lock();
if (pal_stream_handle_ && !is_st_session) {
AHAL_DBG("close stream, pal_stream_handle (%p)",
pal_stream_handle_);
pal_stream_close(pal_stream_handle_);
pal_stream_handle_ = NULL;
}
if (mPalInDeviceIds) {
free(mPalInDeviceIds);
mPalInDeviceIds = NULL;
}
if (mPalInDevice) {
free(mPalInDevice);
mPalInDevice = NULL;
}
stream_mutex_.unlock();
}
StreamPrimary::StreamPrimary(audio_io_handle_t handle,
const std::set<audio_devices_t> &devices __unused, struct audio_config *config):
pal_stream_handle_(NULL),
handle_(handle),
config_(*config),
volume_(NULL),
mmap_shared_memory_fd(-1),
device_cap_query_(NULL)
{
memset(&streamAttributes_, 0, sizeof(streamAttributes_));
memset(&address_, 0, sizeof(address_));
AHAL_DBG("handle: %d channel_mask: %d ", handle_, config_.channel_mask);
}
StreamPrimary::~StreamPrimary(void)
{
if (volume_) {
free(volume_);
volume_ = NULL;
}
if (device_cap_query_) {
if (device_cap_query_->config) {
free(device_cap_query_->config);
device_cap_query_->config = NULL;
}
free(device_cap_query_);
device_cap_query_ = NULL;
}
}