merge in klp-release history after reset to klp-dev
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;