hal: Enable direct playback support for AUDIO_FORMAT_PCM_FLOAT
-Add changes to support PCM_FLOAT format using direct track.
-For targets supporting true 32 bit, convert FLOAT pcm data
to 32 bit.
-Targets which dont support true 32 bit, convert FLOAT or
32 bit input to 24 bit Packed in HAL.
Change-Id: I8052c8b878bbd4d70c0453df054953d494773b0a
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index d6e81b2..7978adf 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -64,6 +64,7 @@
#include <system/thread_defs.h>
#include <audio_effects/effect_aec.h>
#include <audio_effects/effect_ns.h>
+#include <audio_utils/format.h>
#include "audio_hw.h"
#include "platform_api.h"
#include <platform.h>
@@ -164,6 +165,18 @@
.avail_min = AFE_PROXY_RECORD_PERIOD_SIZE,
};
+#define AUDIO_MAX_PCM_FORMATS 7
+
+const uint32_t format_to_bitwidth_table[AUDIO_MAX_PCM_FORMATS] = {
+ [AUDIO_FORMAT_DEFAULT] = 0,
+ [AUDIO_FORMAT_PCM_16_BIT] = sizeof(uint16_t),
+ [AUDIO_FORMAT_PCM_8_BIT] = sizeof(uint8_t),
+ [AUDIO_FORMAT_PCM_32_BIT] = sizeof(uint32_t),
+ [AUDIO_FORMAT_PCM_8_24_BIT] = sizeof(uint32_t),
+ [AUDIO_FORMAT_PCM_FLOAT] = sizeof(float),
+ [AUDIO_FORMAT_PCM_24_BIT_PACKED] = sizeof(uint8_t) * 3,
+};
+
const char * const use_case_table[AUDIO_USECASE_MAX] = {
[USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = "deep-buffer-playback",
[USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = "low-latency-playback",
@@ -337,6 +350,8 @@
format == AUDIO_FORMAT_AAC_ADTS_HE_V2 ||
format == AUDIO_FORMAT_PCM_24_BIT_PACKED ||
format == AUDIO_FORMAT_PCM_8_24_BIT ||
+ format == AUDIO_FORMAT_PCM_FLOAT ||
+ format == AUDIO_FORMAT_PCM_32_BIT ||
format == AUDIO_FORMAT_PCM_16_BIT ||
format == AUDIO_FORMAT_AC3 ||
format == AUDIO_FORMAT_E_AC3 ||
@@ -2123,6 +2138,8 @@
return out->compr_config.fragment_size;
else if(out->usecase == USECASE_COMPRESS_VOIP_CALL)
return voice_extn_compress_voip_out_get_buffer_size(out);
+ else if (out->flags & AUDIO_OUTPUT_FLAG_DIRECT_PCM)
+ return out->compr_pcm_config.hal_fragment_size;
return out->config.period_size *
audio_stream_out_frame_size((const struct audio_stream_out *)stream);
@@ -2592,8 +2609,38 @@
out->is_compr_metadata_avail = false;
}
}
+ if ((out->flags & AUDIO_OUTPUT_FLAG_DIRECT_PCM) &&
+ (out->compr_pcm_config.convert_buffer) != NULL) {
- ret = compress_write(out->compr, buffer, bytes);
+ if ((bytes > out->compr_pcm_config.hal_fragment_size)) {
+ ALOGW("Error written bytes %zu > %d (fragment_size)",
+ bytes, out->compr_pcm_config.hal_fragment_size);
+ pthread_mutex_unlock(&out->lock);
+ return -EINVAL;
+ } else {
+ audio_format_t dst_format = out->compr_pcm_config.hal_op_format;
+ audio_format_t src_format = out->compr_pcm_config.hal_ip_format;
+
+ uint32_t frames = bytes / format_to_bitwidth_table[src_format];
+ uint32_t bytes_to_write = frames * format_to_bitwidth_table[dst_format];
+
+ memcpy_by_audio_format(out->compr_pcm_config.convert_buffer,
+ dst_format,
+ buffer,
+ src_format,
+ frames);
+
+ ret = compress_write(out->compr, out->compr_pcm_config.convert_buffer,
+ bytes_to_write);
+
+ /*Convert written bytes in audio flinger format*/
+ if (ret > 0)
+ ret = ((ret * format_to_bitwidth_table[out->format]) /
+ format_to_bitwidth_table[dst_format]);
+ }
+ } else
+ ret = compress_write(out->compr, buffer, bytes);
+
if (ret < 0)
ret = -errno;
ALOGVV("%s: writing buffer (%zu bytes) to compress device returned %zd", __func__, bytes, ret);
@@ -3316,6 +3363,7 @@
out->handle = handle;
out->bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
out->non_blocking = 0;
+ out->compr_pcm_config.convert_buffer = NULL;
if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL &&
(flags & AUDIO_OUTPUT_FLAG_DIRECT)) {
@@ -3459,19 +3507,6 @@
audio_extn_dolby_set_dmid(adev);
}
- if ((config->offload_info.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM) {
- out->compr_config.fragment_size =
- platform_get_pcm_offload_buffer_size(&config->offload_info);
- out->compr_config.fragments = DIRECT_PCM_NUM_FRAGMENTS;
- } else if (audio_extn_passthru_is_passthrough_stream(out)) {
- out->compr_config.fragment_size =
- audio_extn_passthru_get_buffer_size(&config->offload_info);
- out->compr_config.fragments = COMPRESS_OFFLOAD_NUM_FRAGMENTS;
- } else {
- out->compr_config.fragment_size =
- platform_get_compress_offload_buffer_size(&config->offload_info);
- out->compr_config.fragments = COMPRESS_OFFLOAD_NUM_FRAGMENTS;
- }
out->compr_config.codec->sample_rate =
config->offload_info.sample_rate;
out->compr_config.codec->bit_rate =
@@ -3487,12 +3522,58 @@
out->compr_config.codec->format = SND_AUDIOSTREAMFORMAT_RAW;
if ((config->offload_info.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_AAC_ADTS)
out->compr_config.codec->format = SND_AUDIOSTREAMFORMAT_MP4ADTS;
- if (config->offload_info.format == AUDIO_FORMAT_PCM_16_BIT)
- out->compr_config.codec->format = SNDRV_PCM_FORMAT_S16_LE;
- if (config->offload_info.format == AUDIO_FORMAT_PCM_24_BIT_PACKED)
- out->compr_config.codec->format = SNDRV_PCM_FORMAT_S24_3LE;
- if (config->offload_info.format == AUDIO_FORMAT_PCM_8_24_BIT)
- out->compr_config.codec->format = SNDRV_PCM_FORMAT_S24_LE;
+
+ if ((config->offload_info.format & AUDIO_FORMAT_MAIN_MASK) ==
+ AUDIO_FORMAT_PCM) {
+
+ /*Based on platform support, configure appropriate alsa format for corresponding
+ *hal input format.
+ */
+ out->compr_config.codec->format = hal_format_to_alsa(
+ config->offload_info.format);
+
+ out->compr_pcm_config.hal_op_format = alsa_format_to_hal(
+ out->compr_config.codec->format);
+ out->compr_pcm_config.hal_ip_format = out->format;
+
+ /*for direct PCM playback populate bit_width based on selected alsa format as
+ *hal input format and alsa format might differ based on platform support.
+ */
+ out->bit_width = audio_bytes_per_sample(
+ out->compr_pcm_config.hal_op_format) << 3;
+
+ out->compr_config.fragments = DIRECT_PCM_NUM_FRAGMENTS;
+
+ /* Check if alsa session is configured with the same format as HAL input format,
+ * if not then derive correct fragment size needed to accomodate the
+ * conversion of HAL input format to alsa format.
+ */
+ audio_extn_utils_update_direct_pcm_fragment_size(out);
+
+ /*if hal input and output fragment size is different this indicates HAL input format is
+ *not same as the alsa format
+ */
+ if (out->compr_pcm_config.hal_fragment_size != out->compr_config.fragment_size) {
+ /*Allocate a buffer to convert input data to the alsa configured format.
+ *size of convert buffer is equal to the size required to hold one fragment size
+ *worth of pcm data, this is because flinger does not write more than fragment_size
+ */
+ out->compr_pcm_config.convert_buffer = calloc(1,out->compr_config.fragment_size);
+ if (out->compr_pcm_config.convert_buffer == NULL){
+ ALOGE("Allocation failed for convert buffer for size %d", out->compr_config.fragment_size);
+ ret = -ENOMEM;
+ goto error_open;
+ }
+ }
+ } else if (audio_extn_passthru_is_passthrough_stream(out)) {
+ out->compr_config.fragment_size =
+ audio_extn_passthru_get_buffer_size(&config->offload_info);
+ out->compr_config.fragments = COMPRESS_OFFLOAD_NUM_FRAGMENTS;
+ } else {
+ out->compr_config.fragment_size =
+ platform_get_compress_offload_buffer_size(&config->offload_info);
+ out->compr_config.fragments = COMPRESS_OFFLOAD_NUM_FRAGMENTS;
+ }
if (config->offload_info.format == AUDIO_FORMAT_FLAC)
out->compr_config.codec->options.flac_dec.sample_size = AUDIO_OUTPUT_BIT_WIDTH;
@@ -3652,6 +3733,8 @@
return 0;
error_open:
+ if (out->compr_pcm_config.convert_buffer)
+ free(out->compr_pcm_config.convert_buffer);
free(out);
*stream_out = NULL;
ALOGD("%s: exit: ret %d", __func__, ret);
@@ -3678,6 +3761,8 @@
out_standby(&stream->common);
if (is_offload_usecase(out->usecase)) {
+ if (out->compr_pcm_config.convert_buffer != NULL)
+ free(out->compr_pcm_config.convert_buffer);
audio_extn_dts_remove_state_notifier_node(out->usecase);
destroy_offload_callback_thread(out);
free_offload_usecase(adev, out->usecase);