merge in KQS81M
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index c2997c6..c5e44aa 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1220,7 +1220,7 @@
}
}
- if ((adev->mode != AUDIO_MODE_IN_CALL) && adev->in_call &&
+ if ((adev->mode == AUDIO_MODE_NORMAL) && adev->in_call &&
(out == adev->primary_output)) {
stop_voice_call(adev);
}
@@ -1422,19 +1422,37 @@
{
struct stream_out *out = (struct stream_out *)stream;
int ret = -1;
+ unsigned long dsp_frames;
pthread_mutex_lock(&out->lock);
- if (out->pcm) {
- size_t avail;
- if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) {
- size_t kernel_buffer_size = out->config.period_size * out->config.period_count;
- // FIXME This calculation is incorrect if there is buffering after app processor
- int64_t signed_frames = out->written - kernel_buffer_size + avail;
- // It would be unusual for this value to be negative, but check just in case ...
- if (signed_frames >= 0) {
- *frames = signed_frames;
- ret = 0;
+ if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
+ if (out->compr != NULL) {
+ compress_get_tstamp(out->compr, &dsp_frames,
+ &out->sample_rate);
+ ALOGVV("%s rendered frames %ld sample_rate %d",
+ __func__, dsp_frames, out->sample_rate);
+ *frames = dsp_frames;
+ ret = 0;
+ /* this is the best we can do */
+ clock_gettime(CLOCK_MONOTONIC, timestamp);
+ }
+ } else {
+ if (out->pcm) {
+ size_t avail;
+ if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) {
+ size_t kernel_buffer_size = out->config.period_size * out->config.period_count;
+ int64_t signed_frames = out->written - kernel_buffer_size + avail;
+ // This adjustment accounts for buffering after app processor.
+ // It is based on estimated DSP latency per use case, rather than exact.
+ signed_frames -=
+ (platform_render_latency(out->usecase) * out->sample_rate / 1000000LL);
+
+ // It would be unusual for this value to be negative, but check just in case ...
+ if (signed_frames >= 0) {
+ *frames = signed_frames;
+ ret = 0;
+ }
}
}
}
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index e533f33..b200e27 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -200,6 +200,9 @@
[SND_DEVICE_IN_VOICE_REC_DMIC_BS_FLUENCE] = 5,
};
+#define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL)
+#define LOW_LATENCY_PLATFORM_DELAY (13*1000LL)
+
static pthread_once_t check_op_once_ctl = PTHREAD_ONCE_INIT;
static bool is_tmus = false;
@@ -880,3 +883,16 @@
return max_channels;
}
+
+/* Delay in Us */
+int64_t platform_render_latency(audio_usecase_t usecase)
+{
+ switch (usecase) {
+ case USECASE_AUDIO_PLAYBACK_DEEP_BUFFER:
+ return DEEP_BUFFER_PLATFORM_DELAY;
+ case USECASE_AUDIO_PLAYBACK_LOW_LATENCY:
+ return LOW_LATENCY_PLATFORM_DELAY;
+ default:
+ return 0;
+ }
+}
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index cd7150c..b5d568f 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -185,6 +185,9 @@
[SND_DEVICE_IN_VOICE_REC_DMIC_BS_FLUENCE] = 5,
};
+#define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL)
+#define LOW_LATENCY_PLATFORM_DELAY (13*1000LL)
+
static pthread_once_t check_op_once_ctl = PTHREAD_ONCE_INIT;
static bool is_tmus = false;
@@ -840,3 +843,16 @@
return max_channels;
}
+
+/* Delay in Us */
+int64_t platform_render_latency(audio_usecase_t usecase)
+{
+ switch (usecase) {
+ case USECASE_AUDIO_PLAYBACK_DEEP_BUFFER:
+ return DEEP_BUFFER_PLATFORM_DELAY;
+ case USECASE_AUDIO_PLAYBACK_LOW_LATENCY:
+ return LOW_LATENCY_PLATFORM_DELAY;
+ default:
+ return 0;
+ }
+}
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 2362a5b..afd2ee4 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -36,4 +36,7 @@
int platform_set_hdmi_channels(void *platform, int channel_count);
int platform_edid_get_max_channels(void *platform);
+/* returns the latency for a usecase in Us */
+int64_t platform_render_latency(audio_usecase_t usecase);
+
#endif // QCOM_AUDIO_PLATFORM_API_H
diff --git a/visualizer/offload_visualizer.c b/visualizer/offload_visualizer.c
index 75ce4a5..eb43558 100644
--- a/visualizer/offload_visualizer.c
+++ b/visualizer/offload_visualizer.c
@@ -17,6 +17,7 @@
#define LOG_TAG "offload_visualizer"
/*#define LOG_NDEBUG 0*/
#include <assert.h>
+#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
@@ -80,6 +81,16 @@
#define CAPTURE_BUF_SIZE 65536 /* "64k should be enough for everyone" */
+#define DISCARD_MEASUREMENTS_TIME_MS 2000 /* discard measurements older than this number of ms */
+
+/* maximum number of buffers for which we keep track of the measurements */
+#define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25 /* note: buffer index is stored in uint8_t */
+
+typedef struct buffer_stats_s {
+ bool is_valid;
+ uint16_t peak_u16; /* the positive peak of the absolute value of the samples in a buffer */
+ float rms_squared; /* the average square of the samples in a buffer */
+} buffer_stats_t;
typedef struct visualizer_context_s {
effect_context_t common;
@@ -91,6 +102,12 @@
uint32_t latency;
struct timespec buffer_update_time;
uint8_t capture_buf[CAPTURE_BUF_SIZE];
+ /* for measurements */
+ uint8_t channel_count; /* to avoid recomputing it every time a buffer is processed */
+ uint32_t meas_mode;
+ uint8_t meas_wndw_size_in_buffers;
+ uint8_t meas_buffer_idx;
+ buffer_stats_t past_meas[MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS];
} visualizer_context_t;
@@ -507,6 +524,23 @@
* Visualizer operations
*/
+uint32_t visualizer_get_delta_time_ms_from_updated_time(visualizer_context_t* visu_ctxt) {
+ uint32_t delta_ms = 0;
+ if (visu_ctxt->buffer_update_time.tv_sec != 0) {
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+ time_t secs = ts.tv_sec - visu_ctxt->buffer_update_time.tv_sec;
+ long nsec = ts.tv_nsec - visu_ctxt->buffer_update_time.tv_nsec;
+ if (nsec < 0) {
+ --secs;
+ nsec += 1000000000;
+ }
+ delta_ms = secs * 1000 + nsec / 1000000;
+ }
+ }
+ return delta_ms;
+}
+
int visualizer_reset(effect_context_t *context)
{
visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
@@ -521,6 +555,8 @@
int visualizer_init(effect_context_t *context)
{
+ int32_t i;
+
visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
@@ -543,6 +579,17 @@
visu_ctxt->capture_size = VISUALIZER_CAPTURE_SIZE_MAX;
visu_ctxt->scaling_mode = VISUALIZER_SCALING_MODE_NORMALIZED;
+ // measurement initialization
+ visu_ctxt->channel_count = popcount(context->config.inputCfg.channels);
+ visu_ctxt->meas_mode = MEASUREMENT_MODE_NONE;
+ visu_ctxt->meas_wndw_size_in_buffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS;
+ visu_ctxt->meas_buffer_idx = 0;
+ for (i=0 ; i<visu_ctxt->meas_wndw_size_in_buffers ; i++) {
+ visu_ctxt->past_meas[i].is_valid = false;
+ visu_ctxt->past_meas[i].peak_u16 = 0;
+ visu_ctxt->past_meas[i].rms_squared = 0;
+ }
+
set_config(context, &context->config);
return 0;
@@ -571,6 +618,12 @@
p->vsize = sizeof(uint32_t);
*size += sizeof(uint32_t);
break;
+ case VISUALIZER_PARAM_MEASUREMENT_MODE:
+ ALOGV("%s get meas_mode = %d", __func__, visu_ctxt->meas_mode);
+ *((uint32_t *)p->data + 1) = visu_ctxt->meas_mode;
+ p->vsize = sizeof(uint32_t);
+ *size += sizeof(uint32_t);
+ break;
default:
p->status = -EINVAL;
}
@@ -598,6 +651,10 @@
* visu_ctxt->latency = *((uint32_t *)p->data + 1); */
ALOGV("%s set latency = %d", __func__, visu_ctxt->latency);
break;
+ case VISUALIZER_PARAM_MEASUREMENT_MODE:
+ visu_ctxt->meas_mode = *((uint32_t *)p->data + 1);
+ ALOGV("%s set meas_mode = %d", __func__, visu_ctxt->meas_mode);
+ break;
default:
return -EINVAL;
}
@@ -621,6 +678,30 @@
return -EINVAL;
}
+ // perform measurements if needed
+ if (visu_ctxt->meas_mode & MEASUREMENT_MODE_PEAK_RMS) {
+ // find the peak and RMS squared for the new buffer
+ uint32_t inIdx;
+ int16_t max_sample = 0;
+ float rms_squared_acc = 0;
+ for (inIdx = 0 ; inIdx < inBuffer->frameCount * visu_ctxt->channel_count ; inIdx++) {
+ if (inBuffer->s16[inIdx] > max_sample) {
+ max_sample = inBuffer->s16[inIdx];
+ } else if (-inBuffer->s16[inIdx] > max_sample) {
+ max_sample = -inBuffer->s16[inIdx];
+ }
+ rms_squared_acc += (inBuffer->s16[inIdx] * inBuffer->s16[inIdx]);
+ }
+ // store the measurement
+ visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].peak_u16 = (uint16_t)max_sample;
+ visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].rms_squared =
+ rms_squared_acc / (inBuffer->frameCount * visu_ctxt->channel_count);
+ visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].is_valid = true;
+ if (++visu_ctxt->meas_buffer_idx >= visu_ctxt->meas_wndw_size_in_buffers) {
+ visu_ctxt->meas_buffer_idx = 0;
+ }
+ }
+
/* all code below assumes stereo 16 bit PCM output and input */
int32_t shift;
@@ -700,23 +781,12 @@
if (context->state == EFFECT_STATE_ACTIVE) {
int32_t latency_ms = visu_ctxt->latency;
- uint32_t delta_ms = 0;
- if (visu_ctxt->buffer_update_time.tv_sec != 0) {
- struct timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
- time_t secs = ts.tv_sec - visu_ctxt->buffer_update_time.tv_sec;
- long nsec = ts.tv_nsec - visu_ctxt->buffer_update_time.tv_nsec;
- if (nsec < 0) {
- --secs;
- nsec += 1000000000;
- }
- delta_ms = secs * 1000 + nsec / 1000000;
- latency_ms -= delta_ms;
- if (latency_ms < 0)
- latency_ms = 0;
- }
+ const uint32_t delta_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt);
+ latency_ms -= delta_ms;
+ if (latency_ms < 0) {
+ latency_ms = 0;
}
- uint32_t delta_smp = context->config.inputCfg.samplingRate * latency_ms / 1000;
+ const uint32_t delta_smp = context->config.inputCfg.samplingRate * latency_ms / 1000;
int32_t capture_point = visu_ctxt->capture_idx - visu_ctxt->capture_size - delta_smp;
int32_t capture_size = visu_ctxt->capture_size;
@@ -753,6 +823,56 @@
}
break;
+ case VISUALIZER_CMD_MEASURE: {
+ uint16_t peak_u16 = 0;
+ float sum_rms_squared = 0.0f;
+ uint8_t nb_valid_meas = 0;
+ /* reset measurements if last measurement was too long ago (which implies stored
+ * measurements aren't relevant anymore and shouldn't bias the new one) */
+ const int32_t delay_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt);
+ if (delay_ms > DISCARD_MEASUREMENTS_TIME_MS) {
+ uint32_t i;
+ ALOGV("Discarding measurements, last measurement is %dms old", delay_ms);
+ for (i=0 ; i<visu_ctxt->meas_wndw_size_in_buffers ; i++) {
+ visu_ctxt->past_meas[i].is_valid = false;
+ visu_ctxt->past_meas[i].peak_u16 = 0;
+ visu_ctxt->past_meas[i].rms_squared = 0;
+ }
+ visu_ctxt->meas_buffer_idx = 0;
+ } else {
+ /* only use actual measurements, otherwise the first RMS measure happening before
+ * MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially
+ * low */
+ uint32_t i;
+ for (i=0 ; i < visu_ctxt->meas_wndw_size_in_buffers ; i++) {
+ if (visu_ctxt->past_meas[i].is_valid) {
+ if (visu_ctxt->past_meas[i].peak_u16 > peak_u16) {
+ peak_u16 = visu_ctxt->past_meas[i].peak_u16;
+ }
+ sum_rms_squared += visu_ctxt->past_meas[i].rms_squared;
+ nb_valid_meas++;
+ }
+ }
+ }
+ float rms = nb_valid_meas == 0 ? 0.0f : sqrtf(sum_rms_squared / nb_valid_meas);
+ int32_t* p_int_reply_data = (int32_t*)pReplyData;
+ /* convert from I16 sample values to mB and write results */
+ if (rms < 0.000016f) {
+ p_int_reply_data[MEASUREMENT_IDX_RMS] = -9600; //-96dB
+ } else {
+ p_int_reply_data[MEASUREMENT_IDX_RMS] = (int32_t) (2000 * log10(rms / 32767.0f));
+ }
+ if (peak_u16 == 0) {
+ p_int_reply_data[MEASUREMENT_IDX_PEAK] = -9600; //-96dB
+ } else {
+ p_int_reply_data[MEASUREMENT_IDX_PEAK] = (int32_t) (2000 * log10(peak_u16 / 32767.0f));
+ }
+ ALOGV("VISUALIZER_CMD_MEASURE peak=%d (%dmB), rms=%.1f (%dmB)",
+ peak_u16, p_int_reply_data[MEASUREMENT_IDX_PEAK],
+ rms, p_int_reply_data[MEASUREMENT_IDX_RMS]);
+ }
+ break;
+
default:
ALOGW("%s invalid command %d", __func__, cmdCode);
return -EINVAL;