alsa_sound: add support for low latency playback and recording
b/6865729
- Add deep buffer output in config file
- Configure deep buffer output if flag
AUDIO_OUTPUT_FLAG_DEEP_BUFFER is set,
otherwise configure low latency output.
- Add support for low latency recording
- Enable low latency recording path with
system property
- For 2 buffers and 2048 bytes, reduce
PLAYBACK_LOW_LATENCY to 21.5 ms
Change-Id: I3c0d54fa473fe89df5a3924de483f16975f4000e
Signed-off-by: Iliyan Malchev <malchev@google.com>
diff --git a/alsa_sound/AudioHardwareALSA.cpp b/alsa_sound/AudioHardwareALSA.cpp
index 2bf3fce..232cb43 100644
--- a/alsa_sound/AudioHardwareALSA.cpp
+++ b/alsa_sound/AudioHardwareALSA.cpp
@@ -685,7 +685,10 @@
ALOGV("openOutputStream: devices 0x%x channels %d sampleRate %d",
devices, *channels, *sampleRate);
+ audio_output_flags_t flag = static_cast<audio_output_flags_t> (*status);
+
status_t err = BAD_VALUE;
+ *status = NO_ERROR;
AudioStreamOutALSA *out = 0;
ALSAHandleList::iterator it;
@@ -694,6 +697,8 @@
ALOGE("openOutputStream called with bad devices");
return out;
}
+
+
# if 0
if((devices == AudioSystem::DEVICE_OUT_DIRECTOUTPUT) &&
((*sampleRate == VOIP_SAMPLING_RATE_8K) || (*sampleRate == VOIP_SAMPLING_RATE_16K))) {
@@ -804,13 +809,28 @@
alsa_handle.latency = PLAYBACK_LATENCY;
alsa_handle.rxHandle = 0;
alsa_handle.ucMgr = mUcMgr;
+ alsa_handle.isDeepbufferOutput = false;
char *use_case;
snd_use_case_get(mUcMgr, "_verb", (const char **)&use_case);
- if ((use_case == NULL) || (!strcmp(use_case, SND_USE_CASE_VERB_INACTIVE))) {
- strlcpy(alsa_handle.useCase, SND_USE_CASE_VERB_HIFI, sizeof(alsa_handle.useCase));
+
+ if (flag & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
+ ALOGD("openOutputStream: DeepBuffer Output");
+ alsa_handle.isDeepbufferOutput = true;
+ if ((use_case == NULL) || (!strcmp(use_case, SND_USE_CASE_VERB_INACTIVE))) {
+ strlcpy(alsa_handle.useCase, SND_USE_CASE_VERB_HIFI, sizeof(alsa_handle.useCase));
+ } else {
+ strlcpy(alsa_handle.useCase, SND_USE_CASE_MOD_PLAY_MUSIC, sizeof(alsa_handle.useCase));
+ }
} else {
- strlcpy(alsa_handle.useCase, SND_USE_CASE_MOD_PLAY_MUSIC, sizeof(alsa_handle.useCase));
+ ALOGD("openOutputStream: Lowlatency Output");
+ alsa_handle.bufferSize = PLAYBACK_LOW_LATENCY_BUFFER_SIZE;
+ alsa_handle.latency = PLAYBACK_LOW_LATENCY;
+ if ((use_case == NULL) || (!strcmp(use_case, SND_USE_CASE_VERB_INACTIVE))) {
+ strlcpy(alsa_handle.useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_MUSIC, sizeof(alsa_handle.useCase));
+ } else {
+ strlcpy(alsa_handle.useCase, SND_USE_CASE_MOD_PLAY_LOWLATENCY_MUSIC, sizeof(alsa_handle.useCase));
+ }
}
free(use_case);
mDeviceList.push_back(alsa_handle);
@@ -825,10 +845,18 @@
}
#endif
mALSADevice->route(&(*it), devices, mode());
- if(!strcmp(it->useCase, SND_USE_CASE_VERB_HIFI)) {
- snd_use_case_set(mUcMgr, "_verb", SND_USE_CASE_VERB_HIFI);
+ if (flag & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
+ if(!strcmp(it->useCase, SND_USE_CASE_VERB_HIFI)) {
+ snd_use_case_set(mUcMgr, "_verb", SND_USE_CASE_VERB_HIFI);
+ } else {
+ snd_use_case_set(mUcMgr, "_enamod", SND_USE_CASE_MOD_PLAY_MUSIC);
+ }
} else {
- snd_use_case_set(mUcMgr, "_enamod", SND_USE_CASE_MOD_PLAY_MUSIC);
+ if(!strcmp(it->useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_MUSIC)) {
+ snd_use_case_set(mUcMgr, "_verb", SND_USE_CASE_VERB_HIFI_LOWLATENCY_MUSIC);
+ } else {
+ snd_use_case_set(mUcMgr, "_enamod", SND_USE_CASE_MOD_PLAY_LOWLATENCY_MUSIC);
+ }
}
err = mALSADevice->open(&(*it));
if (err) {
@@ -1068,6 +1096,8 @@
{
if((0 == strncmp(itDev->useCase, SND_USE_CASE_VERB_HIFI_REC, MAX_UC_LEN))
||(0 == strncmp(itDev->useCase, SND_USE_CASE_MOD_CAPTURE_MUSIC, MAX_UC_LEN))
+ ||(0 == strncmp(itDev->useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_REC, MAX_UC_LEN))
+ ||(0 == strncmp(itDev->useCase, SND_USE_CASE_MOD_CAPTURE_LOWLATENCY_MUSIC, MAX_UC_LEN))
||(0 == strncmp(itDev->useCase, SND_USE_CASE_MOD_CAPTURE_FM, MAX_UC_LEN))
#ifdef QCOM_FM_ENABLED
||(0 == strncmp(itDev->useCase, SND_USE_CASE_VERB_FM_REC, MAX_UC_LEN))
@@ -1141,7 +1171,13 @@
strlcpy(alsa_handle.useCase, SND_USE_CASE_MOD_CAPTURE_A2DP_FM, sizeof(alsa_handle.useCase));
#endif
} else {
- strlcpy(alsa_handle.useCase, SND_USE_CASE_MOD_CAPTURE_MUSIC, sizeof(alsa_handle.useCase));
+ char value[128];
+ property_get("persist.audio.lowlatency.rec",value,"0");
+ if (!strcmp("true", value)) {
+ strlcpy(alsa_handle.useCase, SND_USE_CASE_MOD_CAPTURE_LOWLATENCY_MUSIC, sizeof(alsa_handle.useCase));
+ } else {
+ strlcpy(alsa_handle.useCase, SND_USE_CASE_MOD_CAPTURE_MUSIC, sizeof(alsa_handle.useCase));
+ }
}
} else {
if ((devices == AudioSystem::DEVICE_IN_VOICE_CALL) &&
@@ -1175,7 +1211,13 @@
strlcpy(alsa_handle.useCase, SND_USE_CASE_VERB_FM_A2DP_REC, sizeof(alsa_handle.useCase));
#endif
} else {
- strlcpy(alsa_handle.useCase, SND_USE_CASE_VERB_HIFI_REC, sizeof(alsa_handle.useCase));
+ char value[128];
+ property_get("persist.audio.lowlatency.rec",value,"0");
+ if (!strcmp("true", value)) {
+ strlcpy(alsa_handle.useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_REC, sizeof(alsa_handle.useCase));
+ } else {
+ strlcpy(alsa_handle.useCase, SND_USE_CASE_VERB_HIFI_REC, sizeof(alsa_handle.useCase));
+ }
}
}
free(use_case);
@@ -1221,6 +1263,7 @@
}
if(!strcmp(it->useCase, SND_USE_CASE_VERB_HIFI_REC) ||
+ !strcmp(it->useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_REC) ||
#ifdef QCOM_FM_ENABLED
!strcmp(it->useCase, SND_USE_CASE_VERB_FM_REC) ||
!strcmp(it->useCase, SND_USE_CASE_VERB_FM_A2DP_REC) ||
diff --git a/alsa_sound/AudioHardwareALSA.h b/alsa_sound/AudioHardwareALSA.h
index 04eea77..7bf2453 100644
--- a/alsa_sound/AudioHardwareALSA.h
+++ b/alsa_sound/AudioHardwareALSA.h
@@ -61,7 +61,9 @@
#define RECORD_LATENCY 96000
#define VOICE_LATENCY 85333
#define DEFAULT_BUFFER_SIZE 2048
-#define DEFAULT_IN_BUFFER_SIZE 320
+#define PLAYBACK_LOW_LATENCY_BUFFER_SIZE 2048
+#define PLAYBACK_LOW_LATENCY 21500
+#define DEFAULT_IN_BUFFER_SIZE 320
#define FM_BUFFER_SIZE 1024
#define VOIP_SAMPLING_RATE_8K 8000
@@ -164,6 +166,7 @@
unsigned int latency; // Delay in usec
unsigned int bufferSize; // Size of sample buffer
unsigned int periodSize;
+ bool isDeepbufferOutput;
struct pcm * rxHandle;
snd_use_case_mgr_t *ucMgr;
};
diff --git a/alsa_sound/AudioStreamInALSA.cpp b/alsa_sound/AudioStreamInALSA.cpp
index bf16915..e63d606 100644
--- a/alsa_sound/AudioStreamInALSA.cpp
+++ b/alsa_sound/AudioStreamInALSA.cpp
@@ -178,8 +178,14 @@
#endif
} else if(!strcmp(mHandle->useCase, SND_USE_CASE_MOD_PLAY_VOIP)) {
strlcpy(mHandle->useCase, SND_USE_CASE_MOD_PLAY_VOIP, sizeof(mHandle->useCase));
- }else {
- strlcpy(mHandle->useCase, SND_USE_CASE_MOD_CAPTURE_MUSIC, sizeof(mHandle->useCase));
+ } else {
+ char value[128];
+ property_get("persist.audio.lowlatency.rec",value,"0");
+ if (!strcmp("true", value)) {
+ strlcpy(mHandle->useCase, SND_USE_CASE_MOD_CAPTURE_LOWLATENCY_MUSIC, sizeof(mHandle->useCase));
+ } else {
+ strlcpy(mHandle->useCase, SND_USE_CASE_MOD_CAPTURE_MUSIC, sizeof(mHandle->useCase));
+ }
}
} else {
if ((mHandle->devices == AudioSystem::DEVICE_IN_VOICE_CALL) &&
@@ -222,7 +228,13 @@
} else if(!strcmp(mHandle->useCase, SND_USE_CASE_VERB_IP_VOICECALL)){
strlcpy(mHandle->useCase, SND_USE_CASE_VERB_IP_VOICECALL, sizeof(mHandle->useCase));
} else {
- strlcpy(mHandle->useCase, SND_USE_CASE_VERB_HIFI_REC, sizeof(mHandle->useCase));
+ char value[128];
+ property_get("persist.audio.lowlatency.rec",value,"0");
+ if (!strcmp("true", value)) {
+ strlcpy(mHandle->useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_REC, sizeof(mHandle->useCase));
+ } else {
+ strlcpy(mHandle->useCase, SND_USE_CASE_VERB_HIFI_REC, sizeof(mHandle->useCase));
+ }
}
}
free(use_case);
@@ -250,6 +262,7 @@
}
}
if (!strcmp(mHandle->useCase, SND_USE_CASE_VERB_HIFI_REC) ||
+ !strcmp(mHandle->useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_REC) ||
!strcmp(mHandle->useCase, SND_USE_CASE_VERB_FM_REC) ||
!strcmp(mHandle->useCase, SND_USE_CASE_VERB_IP_VOICECALL) ||
!strcmp(mHandle->useCase, SND_USE_CASE_VERB_FM_A2DP_REC) ||
diff --git a/alsa_sound/AudioStreamOutALSA.cpp b/alsa_sound/AudioStreamOutALSA.cpp
index bfc5263..894d9f3 100644
--- a/alsa_sound/AudioStreamOutALSA.cpp
+++ b/alsa_sound/AudioStreamOutALSA.cpp
@@ -131,16 +131,19 @@
if ((use_case == NULL) || (!strcmp(use_case, SND_USE_CASE_VERB_INACTIVE))) {
if(!strcmp(mHandle->useCase, SND_USE_CASE_VERB_IP_VOICECALL)){
strlcpy(mHandle->useCase, SND_USE_CASE_VERB_IP_VOICECALL,sizeof(mHandle->useCase));
- }
- else {
- strlcpy(mHandle->useCase, SND_USE_CASE_VERB_HIFI, sizeof(mHandle->useCase));
- }
+ } else if (mHandle->isDeepbufferOutput){
+ strlcpy(mHandle->useCase, SND_USE_CASE_VERB_HIFI, sizeof(mHandle->useCase));
+ } else {
+ strlcpy(mHandle->useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_MUSIC, sizeof(mHandle->useCase));
+ }
} else {
if(!strcmp(mHandle->useCase, SND_USE_CASE_MOD_PLAY_VOIP)) {
strlcpy(mHandle->useCase, SND_USE_CASE_MOD_PLAY_VOIP,sizeof(mHandle->useCase));
- } else {
- strlcpy(mHandle->useCase, SND_USE_CASE_MOD_PLAY_MUSIC, sizeof(mHandle->useCase));
- }
+ } else if (mHandle->isDeepbufferOutput){
+ strlcpy(mHandle->useCase, SND_USE_CASE_MOD_PLAY_MUSIC, sizeof(mHandle->useCase));
+ } else {
+ strlcpy(mHandle->useCase, SND_USE_CASE_MOD_PLAY_LOWLATENCY_MUSIC, sizeof(mHandle->useCase));
+ }
}
free(use_case);
if((!strcmp(mHandle->useCase, SND_USE_CASE_VERB_IP_VOICECALL)) ||
@@ -168,6 +171,7 @@
mHandle->module->route(mHandle, mDevices , mParent->mode());
}
if (!strcmp(mHandle->useCase, SND_USE_CASE_VERB_HIFI) ||
+ !strcmp(mHandle->useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_MUSIC) ||
!strcmp(mHandle->useCase, SND_USE_CASE_VERB_IP_VOICECALL)) {
snd_use_case_set(mHandle->ucMgr, "_verb", mHandle->useCase);
} else {
diff --git a/alsa_sound/alsa_default.cpp b/alsa_sound/alsa_default.cpp
index 8ff9a0f..0fd9f83 100644
--- a/alsa_sound/alsa_default.cpp
+++ b/alsa_sound/alsa_default.cpp
@@ -625,8 +625,18 @@
// The PCM stream is opened in blocking mode, per ALSA defaults. The
// AudioFlinger seems to assume blocking mode too, so asynchronous mode
// should not be used.
- if ((!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI)) ||
+ if ((!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_LOW_POWER)) ||
+ (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_LPA)) ||
+ (!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_TUNNEL)) ||
+ (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_TUNNEL))) {
+ ALOGV("LPA/tunnel use case");
+ flags |= PCM_MMAP;
+ flags |= DEBUG_ON;
+ } else if ((!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI)) ||
+ (!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_MUSIC)) ||
+ (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_LOWLATENCY_MUSIC)) ||
(!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_MUSIC))) {
+ ALOGV("Music case");
flags = PCM_OUT;
} else {
flags = PCM_IN;
@@ -1151,6 +1161,8 @@
ALOGD("use case is %s\n", useCase);
if (!strncmp(useCase, SND_USE_CASE_VERB_HIFI,
strlen(SND_USE_CASE_VERB_HIFI)) ||
+ !strncmp(useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_MUSIC,
+ strlen(SND_USE_CASE_VERB_HIFI_LOWLATENCY_MUSIC)) ||
!strncmp(useCase, SND_USE_CASE_VERB_HIFI_LOW_POWER,
strlen(SND_USE_CASE_VERB_HIFI_LOW_POWER)) ||
!strncmp(useCase, SND_USE_CASE_VERB_HIFI_TUNNEL,
@@ -1159,6 +1171,8 @@
strlen(SND_USE_CASE_VERB_DIGITAL_RADIO)) ||
!strncmp(useCase, SND_USE_CASE_MOD_PLAY_MUSIC,
strlen(SND_USE_CASE_MOD_PLAY_MUSIC)) ||
+ !strncmp(useCase, SND_USE_CASE_MOD_PLAY_LOWLATENCY_MUSIC,
+ strlen(SND_USE_CASE_MOD_PLAY_LOWLATENCY_MUSIC)) ||
!strncmp(useCase, SND_USE_CASE_MOD_PLAY_LPA,
strlen(SND_USE_CASE_MOD_PLAY_LPA)) ||
!strncmp(useCase, SND_USE_CASE_MOD_PLAY_TUNNEL,
@@ -1168,12 +1182,16 @@
return USECASE_TYPE_RX;
} else if (!strncmp(useCase, SND_USE_CASE_VERB_HIFI_REC,
strlen(SND_USE_CASE_VERB_HIFI_REC)) ||
+ !strncmp(useCase, SND_USE_CASE_VERB_HIFI_LOWLATENCY_REC,
+ strlen(SND_USE_CASE_VERB_HIFI_LOWLATENCY_REC)) ||
!strncmp(useCase, SND_USE_CASE_VERB_FM_REC,
strlen(SND_USE_CASE_VERB_FM_REC)) ||
!strncmp(useCase, SND_USE_CASE_VERB_FM_A2DP_REC,
strlen(SND_USE_CASE_VERB_FM_A2DP_REC)) ||
!strncmp(useCase, SND_USE_CASE_MOD_CAPTURE_MUSIC,
strlen(SND_USE_CASE_MOD_CAPTURE_MUSIC)) ||
+ !strncmp(useCase, SND_USE_CASE_MOD_CAPTURE_LOWLATENCY_MUSIC,
+ strlen(SND_USE_CASE_MOD_CAPTURE_LOWLATENCY_MUSIC)) ||
!strncmp(useCase, SND_USE_CASE_MOD_CAPTURE_FM,
strlen(SND_USE_CASE_MOD_CAPTURE_FM)) ||
!strncmp(useCase, SND_USE_CASE_MOD_CAPTURE_A2DP_FM,
diff --git a/alsa_sound/audio_hw_hal.cpp b/alsa_sound/audio_hw_hal.cpp
index 08f9025..e4e94ed 100644
--- a/alsa_sound/audio_hw_hal.cpp
+++ b/alsa_sound/audio_hw_hal.cpp
@@ -565,6 +565,7 @@
return -ENOMEM;
devices = convert_audio_device(devices, HAL_API_REV_2_0, HAL_API_REV_1_0);
+ status = static_cast<audio_output_flags_t> (flags);
out->qcom_out = qadev->hwif->openOutputStream(devices,
(int *)&config->format,
diff --git a/alsa_sound/audio_policy.conf b/alsa_sound/audio_policy.conf
index 8bfc63d..be2da9d 100644
--- a/alsa_sound/audio_policy.conf
+++ b/alsa_sound/audio_policy.conf
@@ -27,12 +27,19 @@
primary {
outputs {
primary {
- sampling_rates 44100
+ sampling_rates 44100|48000
channel_masks AUDIO_CHANNEL_OUT_STEREO
formats AUDIO_FORMAT_PCM_16_BIT
devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_EARPIECE|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_ALL_SCO|AUDIO_DEVICE_OUT_AUX_DIGITAL
flags AUDIO_OUTPUT_FLAG_PRIMARY
}
+ deep_buffer {
+ sampling_rates 8000|11025|12000|16000|22050|24000|32000|44100|48000
+ channel_masks AUDIO_CHANNEL_OUT_STEREO
+ formats AUDIO_FORMAT_PCM_16_BIT
+ devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_EARPIECE|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_ALL_SCO|AUDIO_DEVICE_OUT_AUX_DIGITAL
+ flags AUDIO_OUTPUT_FLAG_DEEP_BUFFER
+ }
}
inputs {
primary {