blob: ab269ffe5dcc65d43b9be530780ff8c6ff8edce2 [file] [log] [blame]
Eric Laurentc4aef752013-09-12 17:45:53 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "offload_visualizer"
18/*#define LOG_NDEBUG 0*/
19#include <assert.h>
Steven Moreland30faba12017-06-30 15:02:57 -070020#include <dlfcn.h>
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -070021#include <math.h>
Steven Moreland30faba12017-06-30 15:02:57 -070022#include <pthread.h>
Eric Laurentc4aef752013-09-12 17:45:53 -070023#include <stdlib.h>
24#include <string.h>
Eric Laurentc4aef752013-09-12 17:45:53 -070025#include <sys/prctl.h>
Steven Moreland30faba12017-06-30 15:02:57 -070026#include <time.h>
27#include <unistd.h>
Eric Laurentc4aef752013-09-12 17:45:53 -070028
29#include <cutils/list.h>
30#include <cutils/log.h>
31#include <system/thread_defs.h>
32#include <tinyalsa/asoundlib.h>
33#include <audio_effects/effect_visualizer.h>
34
Ravi Kumar Alamanda8652b462014-11-14 16:51:10 -080035#define LIB_ACDB_LOADER "libacdbloader.so"
36#define ACDB_DEV_TYPE_OUT 1
37#define AFE_PROXY_ACDB_ID 45
38
39static void* acdb_handle;
40
41typedef void (*acdb_send_audio_cal_t)(int, int);
42
43acdb_send_audio_cal_t acdb_send_audio_cal;
Eric Laurentc4aef752013-09-12 17:45:53 -070044
45enum {
46 EFFECT_STATE_UNINITIALIZED,
47 EFFECT_STATE_INITIALIZED,
48 EFFECT_STATE_ACTIVE,
49};
50
51typedef struct effect_context_s effect_context_t;
Haynes Mathew George41f86652014-06-17 14:22:15 -070052typedef struct output_context_s output_context_t;
Eric Laurentc4aef752013-09-12 17:45:53 -070053
54/* effect specific operations. Only the init() and process() operations must be defined.
55 * Others are optional.
56 */
57typedef struct effect_ops_s {
58 int (*init)(effect_context_t *context);
59 int (*release)(effect_context_t *context);
60 int (*reset)(effect_context_t *context);
61 int (*enable)(effect_context_t *context);
62 int (*disable)(effect_context_t *context);
Haynes Mathew George41f86652014-06-17 14:22:15 -070063 int (*start)(effect_context_t *context, output_context_t *output);
64 int (*stop)(effect_context_t *context, output_context_t *output);
Eric Laurentc4aef752013-09-12 17:45:53 -070065 int (*process)(effect_context_t *context, audio_buffer_t *in, audio_buffer_t *out);
66 int (*set_parameter)(effect_context_t *context, effect_param_t *param, uint32_t size);
67 int (*get_parameter)(effect_context_t *context, effect_param_t *param, uint32_t *size);
68 int (*command)(effect_context_t *context, uint32_t cmdCode, uint32_t cmdSize,
69 void *pCmdData, uint32_t *replySize, void *pReplyData);
70} effect_ops_t;
71
72struct effect_context_s {
73 const struct effect_interface_s *itfe;
74 struct listnode effects_list_node; /* node in created_effects_list */
75 struct listnode output_node; /* node in output_context_t.effects_list */
76 effect_config_t config;
77 const effect_descriptor_t *desc;
78 audio_io_handle_t out_handle; /* io handle of the output the effect is attached to */
79 uint32_t state;
80 bool offload_enabled; /* when offload is enabled we process VISUALIZER_CMD_CAPTURE command.
81 Otherwise non offloaded visualizer has already processed the command
82 and we must not overwrite the reply. */
83 effect_ops_t ops;
84};
85
David Benjaminc9d8d892016-09-21 12:04:55 -040086struct output_context_s {
Eric Laurentc4aef752013-09-12 17:45:53 -070087 struct listnode outputs_list_node; /* node in active_outputs_list */
88 audio_io_handle_t handle; /* io handle */
89 struct listnode effects_list; /* list of effects attached to this output */
David Benjaminc9d8d892016-09-21 12:04:55 -040090};
Eric Laurentc4aef752013-09-12 17:45:53 -070091
92
93/* maximum time since last capture buffer update before resetting capture buffer. This means
94 that the framework has stopped playing audio and we must start returning silence */
95#define MAX_STALL_TIME_MS 1000
96
97#define CAPTURE_BUF_SIZE 65536 /* "64k should be enough for everyone" */
98
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -070099#define DISCARD_MEASUREMENTS_TIME_MS 2000 /* discard measurements older than this number of ms */
100
101/* maximum number of buffers for which we keep track of the measurements */
102#define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25 /* note: buffer index is stored in uint8_t */
103
104typedef struct buffer_stats_s {
105 bool is_valid;
106 uint16_t peak_u16; /* the positive peak of the absolute value of the samples in a buffer */
107 float rms_squared; /* the average square of the samples in a buffer */
108} buffer_stats_t;
Eric Laurentc4aef752013-09-12 17:45:53 -0700109
110typedef struct visualizer_context_s {
111 effect_context_t common;
112
113 uint32_t capture_idx;
114 uint32_t capture_size;
115 uint32_t scaling_mode;
116 uint32_t last_capture_idx;
117 uint32_t latency;
118 struct timespec buffer_update_time;
119 uint8_t capture_buf[CAPTURE_BUF_SIZE];
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700120 /* for measurements */
121 uint8_t channel_count; /* to avoid recomputing it every time a buffer is processed */
122 uint32_t meas_mode;
123 uint8_t meas_wndw_size_in_buffers;
124 uint8_t meas_buffer_idx;
125 buffer_stats_t past_meas[MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS];
Eric Laurentc4aef752013-09-12 17:45:53 -0700126} visualizer_context_t;
127
128
129extern const struct effect_interface_s effect_interface;
130
131/* Offload visualizer UUID: 7a8044a0-1a71-11e3-a184-0002a5d5c51b */
132const effect_descriptor_t visualizer_descriptor = {
133 {0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
134 {0x7a8044a0, 0x1a71, 0x11e3, 0xa184, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
135 EFFECT_CONTROL_API_VERSION,
136 (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_HW_ACC_TUNNEL ),
137 0, /* TODO */
138 1,
139 "QCOM MSM offload visualizer",
140 "The Android Open Source Project",
141};
142
143const effect_descriptor_t *descriptors[] = {
144 &visualizer_descriptor,
145 NULL,
146};
147
148
149pthread_once_t once = PTHREAD_ONCE_INIT;
150int init_status;
151
152/* list of created effects. Updated by visualizer_hal_start_output()
153 * and visualizer_hal_stop_output() */
154struct listnode created_effects_list;
155/* list of active output streams. Updated by visualizer_hal_start_output()
156 * and visualizer_hal_stop_output() */
157struct listnode active_outputs_list;
158
159/* thread capturing PCM from Proxy port and calling the process function on each enabled effect
160 * attached to an active output stream */
161pthread_t capture_thread;
162/* lock must be held when modifying or accessing created_effects_list or active_outputs_list */
163pthread_mutex_t lock;
164/* thread_lock must be held when starting or stopping the capture thread.
165 * Locking order: thread_lock -> lock */
166pthread_mutex_t thread_lock;
167/* cond is signaled when an output is started or stopped or an effect is enabled or disable: the
168 * capture thread will reevaluate the capture and effect rocess conditions. */
169pthread_cond_t cond;
170/* true when requesting the capture thread to exit */
171bool exit_thread;
172/* 0 if the capture thread was created successfully */
173int thread_status;
174
175
176#define DSP_OUTPUT_LATENCY_MS 0 /* Fudge factor for latency after capture point in audio DSP */
177
178/* Retry for delay for mixer open */
179#define RETRY_NUMBER 10
180#define RETRY_US 500000
181
182#define MIXER_CARD 0
183#define SOUND_CARD 0
184#define CAPTURE_DEVICE 8
185
186/* Proxy port supports only MMAP read and those fixed parameters*/
187#define AUDIO_CAPTURE_CHANNEL_COUNT 2
188#define AUDIO_CAPTURE_SMP_RATE 48000
189#define AUDIO_CAPTURE_PERIOD_SIZE (768)
190#define AUDIO_CAPTURE_PERIOD_COUNT 32
191
192struct pcm_config pcm_config_capture = {
193 .channels = AUDIO_CAPTURE_CHANNEL_COUNT,
194 .rate = AUDIO_CAPTURE_SMP_RATE,
195 .period_size = AUDIO_CAPTURE_PERIOD_SIZE,
196 .period_count = AUDIO_CAPTURE_PERIOD_COUNT,
197 .format = PCM_FORMAT_S16_LE,
198 .start_threshold = AUDIO_CAPTURE_PERIOD_SIZE / 4,
199 .stop_threshold = INT_MAX,
200 .avail_min = AUDIO_CAPTURE_PERIOD_SIZE / 4,
201};
202
203
204/*
205 * Local functions
206 */
207
208static void init_once() {
209 list_init(&created_effects_list);
210 list_init(&active_outputs_list);
211
212 pthread_mutex_init(&lock, NULL);
213 pthread_mutex_init(&thread_lock, NULL);
214 pthread_cond_init(&cond, NULL);
215 exit_thread = false;
216 thread_status = -1;
217
218 init_status = 0;
219}
220
221int lib_init() {
222 pthread_once(&once, init_once);
223 return init_status;
224}
225
226bool effect_exists(effect_context_t *context) {
227 struct listnode *node;
228
229 list_for_each(node, &created_effects_list) {
230 effect_context_t *fx_ctxt = node_to_item(node,
231 effect_context_t,
232 effects_list_node);
233 if (fx_ctxt == context) {
234 return true;
235 }
236 }
237 return false;
238}
239
240output_context_t *get_output(audio_io_handle_t output) {
241 struct listnode *node;
242
243 list_for_each(node, &active_outputs_list) {
244 output_context_t *out_ctxt = node_to_item(node,
245 output_context_t,
246 outputs_list_node);
247 if (out_ctxt->handle == output) {
248 return out_ctxt;
249 }
250 }
251 return NULL;
252}
253
254void add_effect_to_output(output_context_t * output, effect_context_t *context) {
255 struct listnode *fx_node;
256
257 list_for_each(fx_node, &output->effects_list) {
258 effect_context_t *fx_ctxt = node_to_item(fx_node,
259 effect_context_t,
260 output_node);
261 if (fx_ctxt == context)
262 return;
263 }
264 list_add_tail(&output->effects_list, &context->output_node);
Haynes Mathew George41f86652014-06-17 14:22:15 -0700265 if (context->ops.start)
266 context->ops.start(context, output);
Eric Laurentc4aef752013-09-12 17:45:53 -0700267}
268
269void remove_effect_from_output(output_context_t * output, effect_context_t *context) {
270 struct listnode *fx_node;
271
272 list_for_each(fx_node, &output->effects_list) {
273 effect_context_t *fx_ctxt = node_to_item(fx_node,
274 effect_context_t,
275 output_node);
276 if (fx_ctxt == context) {
Haynes Mathew George41f86652014-06-17 14:22:15 -0700277 if (context->ops.stop)
278 context->ops.stop(context, output);
Eric Laurentc4aef752013-09-12 17:45:53 -0700279 list_remove(&context->output_node);
280 return;
281 }
282 }
283}
284
285bool effects_enabled() {
286 struct listnode *out_node;
287
288 list_for_each(out_node, &active_outputs_list) {
289 struct listnode *fx_node;
290 output_context_t *out_ctxt = node_to_item(out_node,
291 output_context_t,
292 outputs_list_node);
293
294 list_for_each(fx_node, &out_ctxt->effects_list) {
295 effect_context_t *fx_ctxt = node_to_item(fx_node,
296 effect_context_t,
297 output_node);
Haynes Mathew George41f86652014-06-17 14:22:15 -0700298 if (fx_ctxt->state == EFFECT_STATE_ACTIVE && fx_ctxt->ops.process != NULL)
Eric Laurentc4aef752013-09-12 17:45:53 -0700299 return true;
300 }
301 }
302 return false;
303}
304
305int configure_proxy_capture(struct mixer *mixer, int value) {
306 const char *proxy_ctl_name = "AFE_PCM_RX Audio Mixer MultiMedia4";
307 struct mixer_ctl *ctl;
308
Ravi Kumar Alamanda8652b462014-11-14 16:51:10 -0800309 if (value && acdb_send_audio_cal)
310 acdb_send_audio_cal(AFE_PROXY_ACDB_ID, ACDB_DEV_TYPE_OUT);
311
Eric Laurentc4aef752013-09-12 17:45:53 -0700312 ctl = mixer_get_ctl_by_name(mixer, proxy_ctl_name);
313 if (ctl == NULL) {
314 ALOGW("%s: could not get %s ctl", __func__, proxy_ctl_name);
315 return -EINVAL;
316 }
317 if (mixer_ctl_set_value(ctl, 0, value) != 0)
318 ALOGW("%s: error setting value %d on %s ", __func__, value, proxy_ctl_name);
319
320 return 0;
321}
322
323
Haynes Mathew Georgecc9649b2014-06-10 15:08:39 -0700324void *capture_thread_loop(void *arg __unused)
Eric Laurentc4aef752013-09-12 17:45:53 -0700325{
326 int16_t data[AUDIO_CAPTURE_PERIOD_SIZE * AUDIO_CAPTURE_CHANNEL_COUNT * sizeof(int16_t)];
327 audio_buffer_t buf;
328 buf.frameCount = AUDIO_CAPTURE_PERIOD_SIZE;
329 buf.s16 = data;
330 bool capture_enabled = false;
331 struct mixer *mixer;
332 struct pcm *pcm = NULL;
333 int ret;
334 int retry_num = 0;
335
336 ALOGD("thread enter");
337
338 prctl(PR_SET_NAME, (unsigned long)"visualizer capture", 0, 0, 0);
339
340 pthread_mutex_lock(&lock);
341
342 mixer = mixer_open(MIXER_CARD);
343 while (mixer == NULL && retry_num < RETRY_NUMBER) {
344 usleep(RETRY_US);
345 mixer = mixer_open(MIXER_CARD);
346 retry_num++;
347 }
348 if (mixer == NULL) {
349 pthread_mutex_unlock(&lock);
350 return NULL;
351 }
352
353 for (;;) {
354 if (exit_thread) {
355 break;
356 }
357 if (effects_enabled()) {
358 if (!capture_enabled) {
359 ret = configure_proxy_capture(mixer, 1);
360 if (ret == 0) {
361 pcm = pcm_open(SOUND_CARD, CAPTURE_DEVICE,
362 PCM_IN|PCM_MMAP|PCM_NOIRQ, &pcm_config_capture);
363 if (pcm && !pcm_is_ready(pcm)) {
364 ALOGW("%s: %s", __func__, pcm_get_error(pcm));
365 pcm_close(pcm);
366 pcm = NULL;
367 configure_proxy_capture(mixer, 0);
368 } else {
369 capture_enabled = true;
370 ALOGD("%s: capture ENABLED", __func__);
371 }
372 }
373 }
374 } else {
375 if (capture_enabled) {
376 if (pcm != NULL)
377 pcm_close(pcm);
378 configure_proxy_capture(mixer, 0);
379 ALOGD("%s: capture DISABLED", __func__);
380 capture_enabled = false;
381 }
382 pthread_cond_wait(&cond, &lock);
383 }
384 if (!capture_enabled)
385 continue;
386
387 pthread_mutex_unlock(&lock);
388 ret = pcm_mmap_read(pcm, data, sizeof(data));
389 pthread_mutex_lock(&lock);
390
391 if (ret == 0) {
392 struct listnode *out_node;
393
394 list_for_each(out_node, &active_outputs_list) {
395 output_context_t *out_ctxt = node_to_item(out_node,
396 output_context_t,
397 outputs_list_node);
398 struct listnode *fx_node;
399
400 list_for_each(fx_node, &out_ctxt->effects_list) {
401 effect_context_t *fx_ctxt = node_to_item(fx_node,
402 effect_context_t,
403 output_node);
Haynes Mathew George41f86652014-06-17 14:22:15 -0700404 if (fx_ctxt->ops.process != NULL)
405 fx_ctxt->ops.process(fx_ctxt, &buf, &buf);
Eric Laurentc4aef752013-09-12 17:45:53 -0700406 }
407 }
408 } else {
409 ALOGW("%s: read status %d %s", __func__, ret, pcm_get_error(pcm));
410 }
411 }
412
413 if (capture_enabled) {
414 if (pcm != NULL)
415 pcm_close(pcm);
416 configure_proxy_capture(mixer, 0);
417 }
418 mixer_close(mixer);
419 pthread_mutex_unlock(&lock);
420
421 ALOGD("thread exit");
422
423 return NULL;
424}
425
426/*
427 * Interface from audio HAL
428 */
429
430__attribute__ ((visibility ("default")))
Haynes Mathew George41f86652014-06-17 14:22:15 -0700431int visualizer_hal_start_output(audio_io_handle_t output, int pcm_id) {
David Benjaminc9d8d892016-09-21 12:04:55 -0400432 int ret = 0;
Eric Laurentc4aef752013-09-12 17:45:53 -0700433 struct listnode *node;
434
Haynes Mathew George41f86652014-06-17 14:22:15 -0700435 ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
Eric Laurentc4aef752013-09-12 17:45:53 -0700436
437 if (lib_init() != 0)
438 return init_status;
439
440 pthread_mutex_lock(&thread_lock);
441 pthread_mutex_lock(&lock);
442 if (get_output(output) != NULL) {
443 ALOGW("%s output already started", __func__);
444 ret = -ENOSYS;
445 goto exit;
446 }
447
448 output_context_t *out_ctxt = (output_context_t *)malloc(sizeof(output_context_t));
449 out_ctxt->handle = output;
450 list_init(&out_ctxt->effects_list);
451
452 list_for_each(node, &created_effects_list) {
453 effect_context_t *fx_ctxt = node_to_item(node,
454 effect_context_t,
455 effects_list_node);
456 if (fx_ctxt->out_handle == output) {
Haynes Mathew George41f86652014-06-17 14:22:15 -0700457 if (fx_ctxt->ops.start)
458 fx_ctxt->ops.start(fx_ctxt, out_ctxt);
Eric Laurentc4aef752013-09-12 17:45:53 -0700459 list_add_tail(&out_ctxt->effects_list, &fx_ctxt->output_node);
460 }
461 }
462 if (list_empty(&active_outputs_list)) {
463 exit_thread = false;
464 thread_status = pthread_create(&capture_thread, (const pthread_attr_t *) NULL,
465 capture_thread_loop, NULL);
466 }
467 list_add_tail(&active_outputs_list, &out_ctxt->outputs_list_node);
468 pthread_cond_signal(&cond);
469
470exit:
471 pthread_mutex_unlock(&lock);
472 pthread_mutex_unlock(&thread_lock);
473 return ret;
474}
475
476__attribute__ ((visibility ("default")))
Haynes Mathew George41f86652014-06-17 14:22:15 -0700477int visualizer_hal_stop_output(audio_io_handle_t output, int pcm_id) {
David Benjaminc9d8d892016-09-21 12:04:55 -0400478 int ret = 0;
Eric Laurentc4aef752013-09-12 17:45:53 -0700479 struct listnode *node;
Haynes Mathew George41f86652014-06-17 14:22:15 -0700480 struct listnode *fx_node;
Eric Laurentc4aef752013-09-12 17:45:53 -0700481 output_context_t *out_ctxt;
482
Haynes Mathew George41f86652014-06-17 14:22:15 -0700483 ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
Eric Laurentc4aef752013-09-12 17:45:53 -0700484
485 if (lib_init() != 0)
486 return init_status;
487
488 pthread_mutex_lock(&thread_lock);
489 pthread_mutex_lock(&lock);
490
491 out_ctxt = get_output(output);
492 if (out_ctxt == NULL) {
493 ALOGW("%s output not started", __func__);
494 ret = -ENOSYS;
495 goto exit;
496 }
Haynes Mathew George41f86652014-06-17 14:22:15 -0700497 list_for_each(fx_node, &out_ctxt->effects_list) {
498 effect_context_t *fx_ctxt = node_to_item(fx_node,
499 effect_context_t,
500 output_node);
501 if (fx_ctxt->ops.stop)
502 fx_ctxt->ops.stop(fx_ctxt, out_ctxt);
503 }
Eric Laurentc4aef752013-09-12 17:45:53 -0700504 list_remove(&out_ctxt->outputs_list_node);
505 pthread_cond_signal(&cond);
506
507 if (list_empty(&active_outputs_list)) {
508 if (thread_status == 0) {
509 exit_thread = true;
510 pthread_cond_signal(&cond);
511 pthread_mutex_unlock(&lock);
512 pthread_join(capture_thread, (void **) NULL);
513 pthread_mutex_lock(&lock);
514 thread_status = -1;
515 }
516 }
517
518 free(out_ctxt);
519
520exit:
521 pthread_mutex_unlock(&lock);
522 pthread_mutex_unlock(&thread_lock);
523 return ret;
524}
525
526
527/*
528 * Effect operations
529 */
530
531int set_config(effect_context_t *context, effect_config_t *config)
532{
533 if (config->inputCfg.samplingRate != config->outputCfg.samplingRate) return -EINVAL;
534 if (config->inputCfg.channels != config->outputCfg.channels) return -EINVAL;
535 if (config->inputCfg.format != config->outputCfg.format) return -EINVAL;
536 if (config->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
537 if (config->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
538 config->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
539 if (config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL;
540
541 context->config = *config;
542
543 if (context->ops.reset)
544 context->ops.reset(context);
545
546 return 0;
547}
548
549void get_config(effect_context_t *context, effect_config_t *config)
550{
551 *config = context->config;
552}
553
554
555/*
556 * Visualizer operations
557 */
558
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700559uint32_t visualizer_get_delta_time_ms_from_updated_time(visualizer_context_t* visu_ctxt) {
560 uint32_t delta_ms = 0;
561 if (visu_ctxt->buffer_update_time.tv_sec != 0) {
562 struct timespec ts;
563 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
564 time_t secs = ts.tv_sec - visu_ctxt->buffer_update_time.tv_sec;
565 long nsec = ts.tv_nsec - visu_ctxt->buffer_update_time.tv_nsec;
566 if (nsec < 0) {
567 --secs;
568 nsec += 1000000000;
569 }
570 delta_ms = secs * 1000 + nsec / 1000000;
571 }
572 }
573 return delta_ms;
574}
575
Eric Laurentc4aef752013-09-12 17:45:53 -0700576int visualizer_reset(effect_context_t *context)
577{
578 visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
579
580 visu_ctxt->capture_idx = 0;
581 visu_ctxt->last_capture_idx = 0;
582 visu_ctxt->buffer_update_time.tv_sec = 0;
583 visu_ctxt->latency = DSP_OUTPUT_LATENCY_MS;
584 memset(visu_ctxt->capture_buf, 0x80, CAPTURE_BUF_SIZE);
585 return 0;
586}
587
588int visualizer_init(effect_context_t *context)
589{
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700590 int32_t i;
591
Eric Laurentc4aef752013-09-12 17:45:53 -0700592 visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
593
594 context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
595 context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
596 context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
597 context->config.inputCfg.samplingRate = 44100;
598 context->config.inputCfg.bufferProvider.getBuffer = NULL;
599 context->config.inputCfg.bufferProvider.releaseBuffer = NULL;
600 context->config.inputCfg.bufferProvider.cookie = NULL;
601 context->config.inputCfg.mask = EFFECT_CONFIG_ALL;
602 context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
603 context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
604 context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
605 context->config.outputCfg.samplingRate = 44100;
606 context->config.outputCfg.bufferProvider.getBuffer = NULL;
607 context->config.outputCfg.bufferProvider.releaseBuffer = NULL;
608 context->config.outputCfg.bufferProvider.cookie = NULL;
609 context->config.outputCfg.mask = EFFECT_CONFIG_ALL;
610
611 visu_ctxt->capture_size = VISUALIZER_CAPTURE_SIZE_MAX;
612 visu_ctxt->scaling_mode = VISUALIZER_SCALING_MODE_NORMALIZED;
613
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700614 // measurement initialization
Eric Laurent0de8d1f2014-07-01 20:34:45 -0700615 visu_ctxt->channel_count = audio_channel_count_from_out_mask(context->config.inputCfg.channels);
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700616 visu_ctxt->meas_mode = MEASUREMENT_MODE_NONE;
617 visu_ctxt->meas_wndw_size_in_buffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS;
618 visu_ctxt->meas_buffer_idx = 0;
619 for (i=0 ; i<visu_ctxt->meas_wndw_size_in_buffers ; i++) {
620 visu_ctxt->past_meas[i].is_valid = false;
621 visu_ctxt->past_meas[i].peak_u16 = 0;
622 visu_ctxt->past_meas[i].rms_squared = 0;
623 }
624
Eric Laurentc4aef752013-09-12 17:45:53 -0700625 set_config(context, &context->config);
626
Ravi Kumar Alamanda8652b462014-11-14 16:51:10 -0800627 if (acdb_handle == NULL) {
628 acdb_handle = dlopen(LIB_ACDB_LOADER, RTLD_NOW);
629 if (acdb_handle == NULL) {
630 ALOGE("%s: DLOPEN failed for %s", __func__, LIB_ACDB_LOADER);
631 } else {
632 acdb_send_audio_cal = (acdb_send_audio_cal_t)dlsym(acdb_handle,
633 "acdb_loader_send_audio_cal");
634 if (!acdb_send_audio_cal)
635 ALOGE("%s: Could not find the symbol acdb_send_audio_cal from %s",
636 __func__, LIB_ACDB_LOADER);
637 }
638 }
639
Eric Laurentc4aef752013-09-12 17:45:53 -0700640 return 0;
641}
642
643int visualizer_get_parameter(effect_context_t *context, effect_param_t *p, uint32_t *size)
644{
645 visualizer_context_t *visu_ctxt = (visualizer_context_t *)context;
646
647 p->status = 0;
648 *size = sizeof(effect_param_t) + sizeof(uint32_t);
649 if (p->psize != sizeof(uint32_t)) {
650 p->status = -EINVAL;
651 return 0;
652 }
653 switch (*(uint32_t *)p->data) {
654 case VISUALIZER_PARAM_CAPTURE_SIZE:
655 ALOGV("%s get capture_size = %d", __func__, visu_ctxt->capture_size);
656 *((uint32_t *)p->data + 1) = visu_ctxt->capture_size;
657 p->vsize = sizeof(uint32_t);
658 *size += sizeof(uint32_t);
659 break;
660 case VISUALIZER_PARAM_SCALING_MODE:
661 ALOGV("%s get scaling_mode = %d", __func__, visu_ctxt->scaling_mode);
662 *((uint32_t *)p->data + 1) = visu_ctxt->scaling_mode;
663 p->vsize = sizeof(uint32_t);
664 *size += sizeof(uint32_t);
665 break;
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700666 case VISUALIZER_PARAM_MEASUREMENT_MODE:
667 ALOGV("%s get meas_mode = %d", __func__, visu_ctxt->meas_mode);
668 *((uint32_t *)p->data + 1) = visu_ctxt->meas_mode;
669 p->vsize = sizeof(uint32_t);
670 *size += sizeof(uint32_t);
671 break;
Eric Laurentc4aef752013-09-12 17:45:53 -0700672 default:
673 p->status = -EINVAL;
674 }
675 return 0;
676}
677
Haynes Mathew Georgecc9649b2014-06-10 15:08:39 -0700678int visualizer_set_parameter(effect_context_t *context, effect_param_t *p, uint32_t size __unused)
Eric Laurentc4aef752013-09-12 17:45:53 -0700679{
680 visualizer_context_t *visu_ctxt = (visualizer_context_t *)context;
681
682 if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t))
683 return -EINVAL;
684
685 switch (*(uint32_t *)p->data) {
686 case VISUALIZER_PARAM_CAPTURE_SIZE:
687 visu_ctxt->capture_size = *((uint32_t *)p->data + 1);
688 ALOGV("%s set capture_size = %d", __func__, visu_ctxt->capture_size);
689 break;
690 case VISUALIZER_PARAM_SCALING_MODE:
691 visu_ctxt->scaling_mode = *((uint32_t *)p->data + 1);
692 ALOGV("%s set scaling_mode = %d", __func__, visu_ctxt->scaling_mode);
693 break;
694 case VISUALIZER_PARAM_LATENCY:
695 /* Ignore latency as we capture at DSP output
696 * visu_ctxt->latency = *((uint32_t *)p->data + 1); */
697 ALOGV("%s set latency = %d", __func__, visu_ctxt->latency);
698 break;
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700699 case VISUALIZER_PARAM_MEASUREMENT_MODE:
700 visu_ctxt->meas_mode = *((uint32_t *)p->data + 1);
701 ALOGV("%s set meas_mode = %d", __func__, visu_ctxt->meas_mode);
702 break;
Eric Laurentc4aef752013-09-12 17:45:53 -0700703 default:
704 return -EINVAL;
705 }
706 return 0;
707}
708
709/* Real process function called from capture thread. Called with lock held */
710int visualizer_process(effect_context_t *context,
711 audio_buffer_t *inBuffer,
712 audio_buffer_t *outBuffer)
713{
714 visualizer_context_t *visu_ctxt = (visualizer_context_t *)context;
715
716 if (!effect_exists(context))
717 return -EINVAL;
718
719 if (inBuffer == NULL || inBuffer->raw == NULL ||
720 outBuffer == NULL || outBuffer->raw == NULL ||
721 inBuffer->frameCount != outBuffer->frameCount ||
722 inBuffer->frameCount == 0) {
723 return -EINVAL;
724 }
725
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700726 // perform measurements if needed
727 if (visu_ctxt->meas_mode & MEASUREMENT_MODE_PEAK_RMS) {
728 // find the peak and RMS squared for the new buffer
729 uint32_t inIdx;
730 int16_t max_sample = 0;
731 float rms_squared_acc = 0;
732 for (inIdx = 0 ; inIdx < inBuffer->frameCount * visu_ctxt->channel_count ; inIdx++) {
733 if (inBuffer->s16[inIdx] > max_sample) {
734 max_sample = inBuffer->s16[inIdx];
735 } else if (-inBuffer->s16[inIdx] > max_sample) {
736 max_sample = -inBuffer->s16[inIdx];
737 }
738 rms_squared_acc += (inBuffer->s16[inIdx] * inBuffer->s16[inIdx]);
739 }
740 // store the measurement
741 visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].peak_u16 = (uint16_t)max_sample;
742 visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].rms_squared =
743 rms_squared_acc / (inBuffer->frameCount * visu_ctxt->channel_count);
744 visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].is_valid = true;
745 if (++visu_ctxt->meas_buffer_idx >= visu_ctxt->meas_wndw_size_in_buffers) {
746 visu_ctxt->meas_buffer_idx = 0;
747 }
748 }
749
Eric Laurentc4aef752013-09-12 17:45:53 -0700750 /* all code below assumes stereo 16 bit PCM output and input */
751 int32_t shift;
752
753 if (visu_ctxt->scaling_mode == VISUALIZER_SCALING_MODE_NORMALIZED) {
754 /* derive capture scaling factor from peak value in current buffer
755 * this gives more interesting captures for display. */
756 shift = 32;
757 int len = inBuffer->frameCount * 2;
758 int i;
759 for (i = 0; i < len; i++) {
760 int32_t smp = inBuffer->s16[i];
761 if (smp < 0) smp = -smp - 1; /* take care to keep the max negative in range */
762 int32_t clz = __builtin_clz(smp);
763 if (shift > clz) shift = clz;
764 }
765 /* A maximum amplitude signal will have 17 leading zeros, which we want to
766 * translate to a shift of 8 (for converting 16 bit to 8 bit) */
767 shift = 25 - shift;
768 /* Never scale by less than 8 to avoid returning unaltered PCM signal. */
769 if (shift < 3) {
770 shift = 3;
771 }
772 /* add one to combine the division by 2 needed after summing
773 * left and right channels below */
774 shift++;
775 } else {
776 assert(visu_ctxt->scaling_mode == VISUALIZER_SCALING_MODE_AS_PLAYED);
777 shift = 9;
778 }
779
780 uint32_t capt_idx;
781 uint32_t in_idx;
782 uint8_t *buf = visu_ctxt->capture_buf;
783 for (in_idx = 0, capt_idx = visu_ctxt->capture_idx;
784 in_idx < inBuffer->frameCount;
785 in_idx++, capt_idx++) {
786 if (capt_idx >= CAPTURE_BUF_SIZE) {
787 /* wrap around */
788 capt_idx = 0;
789 }
790 int32_t smp = inBuffer->s16[2 * in_idx] + inBuffer->s16[2 * in_idx + 1];
791 smp = smp >> shift;
792 buf[capt_idx] = ((uint8_t)smp)^0x80;
793 }
794
795 /* XXX the following two should really be atomic, though it probably doesn't
796 * matter much for visualization purposes */
797 visu_ctxt->capture_idx = capt_idx;
798 /* update last buffer update time stamp */
799 if (clock_gettime(CLOCK_MONOTONIC, &visu_ctxt->buffer_update_time) < 0) {
800 visu_ctxt->buffer_update_time.tv_sec = 0;
801 }
802
803 if (context->state != EFFECT_STATE_ACTIVE) {
804 ALOGV("%s DONE inactive", __func__);
805 return -ENODATA;
806 }
807
808 return 0;
809}
810
Haynes Mathew Georgecc9649b2014-06-10 15:08:39 -0700811int visualizer_command(effect_context_t * context, uint32_t cmdCode, uint32_t cmdSize __unused,
812 void *pCmdData __unused, uint32_t *replySize, void *pReplyData)
Eric Laurentc4aef752013-09-12 17:45:53 -0700813{
814 visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
815
816 switch (cmdCode) {
817 case VISUALIZER_CMD_CAPTURE:
818 if (pReplyData == NULL || *replySize != visu_ctxt->capture_size) {
819 ALOGV("%s VISUALIZER_CMD_CAPTURE error *replySize %d context->capture_size %d",
820 __func__, *replySize, visu_ctxt->capture_size);
821 return -EINVAL;
822 }
823
824 if (!context->offload_enabled)
825 break;
826
827 if (context->state == EFFECT_STATE_ACTIVE) {
828 int32_t latency_ms = visu_ctxt->latency;
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700829 const uint32_t delta_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt);
830 latency_ms -= delta_ms;
831 if (latency_ms < 0) {
832 latency_ms = 0;
Eric Laurentc4aef752013-09-12 17:45:53 -0700833 }
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700834 const uint32_t delta_smp = context->config.inputCfg.samplingRate * latency_ms / 1000;
Eric Laurentc4aef752013-09-12 17:45:53 -0700835
836 int32_t capture_point = visu_ctxt->capture_idx - visu_ctxt->capture_size - delta_smp;
837 int32_t capture_size = visu_ctxt->capture_size;
838 if (capture_point < 0) {
839 int32_t size = -capture_point;
840 if (size > capture_size)
841 size = capture_size;
842
843 memcpy(pReplyData,
844 visu_ctxt->capture_buf + CAPTURE_BUF_SIZE + capture_point,
845 size);
846 pReplyData = (void *)((size_t)pReplyData + size);
847 capture_size -= size;
848 capture_point = 0;
849 }
850 memcpy(pReplyData,
851 visu_ctxt->capture_buf + capture_point,
852 capture_size);
853
854
855 /* if audio framework has stopped playing audio although the effect is still
856 * active we must clear the capture buffer to return silence */
857 if ((visu_ctxt->last_capture_idx == visu_ctxt->capture_idx) &&
858 (visu_ctxt->buffer_update_time.tv_sec != 0)) {
859 if (delta_ms > MAX_STALL_TIME_MS) {
860 ALOGV("%s capture going to idle", __func__);
861 visu_ctxt->buffer_update_time.tv_sec = 0;
862 memset(pReplyData, 0x80, visu_ctxt->capture_size);
863 }
864 }
865 visu_ctxt->last_capture_idx = visu_ctxt->capture_idx;
866 } else {
867 memset(pReplyData, 0x80, visu_ctxt->capture_size);
868 }
869 break;
870
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700871 case VISUALIZER_CMD_MEASURE: {
rago2fa52192016-08-22 17:59:38 -0700872 if (pReplyData == NULL || replySize == NULL ||
873 *replySize < (sizeof(int32_t) * MEASUREMENT_COUNT)) {
rago314274d2016-10-07 18:13:29 -0700874 if (replySize == NULL) {
875 ALOGV("%s VISUALIZER_CMD_MEASURE error replySize NULL", __func__);
876 } else {
877 ALOGV("%s VISUALIZER_CMD_MEASURE error *replySize %u <"
878 "(sizeof(int32_t) * MEASUREMENT_COUNT) %zu",
879 __func__, *replySize, sizeof(int32_t) * MEASUREMENT_COUNT);
880 }
rago2fa52192016-08-22 17:59:38 -0700881 android_errorWriteLog(0x534e4554, "30229821");
882 return -EINVAL;
883 }
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700884 uint16_t peak_u16 = 0;
885 float sum_rms_squared = 0.0f;
886 uint8_t nb_valid_meas = 0;
887 /* reset measurements if last measurement was too long ago (which implies stored
888 * measurements aren't relevant anymore and shouldn't bias the new one) */
889 const int32_t delay_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt);
890 if (delay_ms > DISCARD_MEASUREMENTS_TIME_MS) {
891 uint32_t i;
892 ALOGV("Discarding measurements, last measurement is %dms old", delay_ms);
893 for (i=0 ; i<visu_ctxt->meas_wndw_size_in_buffers ; i++) {
894 visu_ctxt->past_meas[i].is_valid = false;
895 visu_ctxt->past_meas[i].peak_u16 = 0;
896 visu_ctxt->past_meas[i].rms_squared = 0;
897 }
898 visu_ctxt->meas_buffer_idx = 0;
899 } else {
900 /* only use actual measurements, otherwise the first RMS measure happening before
901 * MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially
902 * low */
903 uint32_t i;
904 for (i=0 ; i < visu_ctxt->meas_wndw_size_in_buffers ; i++) {
905 if (visu_ctxt->past_meas[i].is_valid) {
906 if (visu_ctxt->past_meas[i].peak_u16 > peak_u16) {
907 peak_u16 = visu_ctxt->past_meas[i].peak_u16;
908 }
909 sum_rms_squared += visu_ctxt->past_meas[i].rms_squared;
910 nb_valid_meas++;
911 }
912 }
913 }
914 float rms = nb_valid_meas == 0 ? 0.0f : sqrtf(sum_rms_squared / nb_valid_meas);
915 int32_t* p_int_reply_data = (int32_t*)pReplyData;
916 /* convert from I16 sample values to mB and write results */
917 if (rms < 0.000016f) {
918 p_int_reply_data[MEASUREMENT_IDX_RMS] = -9600; //-96dB
919 } else {
920 p_int_reply_data[MEASUREMENT_IDX_RMS] = (int32_t) (2000 * log10(rms / 32767.0f));
921 }
922 if (peak_u16 == 0) {
923 p_int_reply_data[MEASUREMENT_IDX_PEAK] = -9600; //-96dB
924 } else {
925 p_int_reply_data[MEASUREMENT_IDX_PEAK] = (int32_t) (2000 * log10(peak_u16 / 32767.0f));
926 }
927 ALOGV("VISUALIZER_CMD_MEASURE peak=%d (%dmB), rms=%.1f (%dmB)",
928 peak_u16, p_int_reply_data[MEASUREMENT_IDX_PEAK],
929 rms, p_int_reply_data[MEASUREMENT_IDX_RMS]);
930 }
931 break;
932
Eric Laurentc4aef752013-09-12 17:45:53 -0700933 default:
934 ALOGW("%s invalid command %d", __func__, cmdCode);
935 return -EINVAL;
936 }
937 return 0;
938}
939
940
941/*
942 * Effect Library Interface Implementation
943 */
944
945int effect_lib_create(const effect_uuid_t *uuid,
Haynes Mathew Georgecc9649b2014-06-10 15:08:39 -0700946 int32_t sessionId __unused,
Eric Laurentc4aef752013-09-12 17:45:53 -0700947 int32_t ioId,
948 effect_handle_t *pHandle) {
949 int ret;
950 int i;
951
952 if (lib_init() != 0)
953 return init_status;
954
955 if (pHandle == NULL || uuid == NULL)
956 return -EINVAL;
957
958 for (i = 0; descriptors[i] != NULL; i++) {
959 if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0)
960 break;
961 }
962
963 if (descriptors[i] == NULL)
964 return -EINVAL;
965
966 effect_context_t *context;
967 if (memcmp(uuid, &visualizer_descriptor.uuid, sizeof(effect_uuid_t)) == 0) {
968 visualizer_context_t *visu_ctxt = (visualizer_context_t *)calloc(1,
969 sizeof(visualizer_context_t));
970 context = (effect_context_t *)visu_ctxt;
971 context->ops.init = visualizer_init;
972 context->ops.reset = visualizer_reset;
973 context->ops.process = visualizer_process;
974 context->ops.set_parameter = visualizer_set_parameter;
975 context->ops.get_parameter = visualizer_get_parameter;
976 context->ops.command = visualizer_command;
Haynes Mathew George41f86652014-06-17 14:22:15 -0700977 context->desc = &visualizer_descriptor;
Eric Laurentc4aef752013-09-12 17:45:53 -0700978 } else {
979 return -EINVAL;
980 }
981
982 context->itfe = &effect_interface;
983 context->state = EFFECT_STATE_UNINITIALIZED;
984 context->out_handle = (audio_io_handle_t)ioId;
Eric Laurentc4aef752013-09-12 17:45:53 -0700985
986 ret = context->ops.init(context);
987 if (ret < 0) {
988 ALOGW("%s init failed", __func__);
989 free(context);
990 return ret;
991 }
992
993 context->state = EFFECT_STATE_INITIALIZED;
994
995 pthread_mutex_lock(&lock);
996 list_add_tail(&created_effects_list, &context->effects_list_node);
997 output_context_t *out_ctxt = get_output(ioId);
998 if (out_ctxt != NULL)
999 add_effect_to_output(out_ctxt, context);
1000 pthread_mutex_unlock(&lock);
1001
1002 *pHandle = (effect_handle_t)context;
1003
1004 ALOGV("%s created context %p", __func__, context);
1005
1006 return 0;
1007
1008}
1009
1010int effect_lib_release(effect_handle_t handle) {
1011 effect_context_t *context = (effect_context_t *)handle;
1012 int status;
1013
1014 if (lib_init() != 0)
1015 return init_status;
1016
1017 ALOGV("%s context %p", __func__, handle);
1018 pthread_mutex_lock(&lock);
1019 status = -EINVAL;
1020 if (effect_exists(context)) {
1021 output_context_t *out_ctxt = get_output(context->out_handle);
1022 if (out_ctxt != NULL)
1023 remove_effect_from_output(out_ctxt, context);
1024 list_remove(&context->effects_list_node);
1025 if (context->ops.release)
1026 context->ops.release(context);
1027 free(context);
1028 status = 0;
1029 }
1030 pthread_mutex_unlock(&lock);
1031
1032 return status;
1033}
1034
1035int effect_lib_get_descriptor(const effect_uuid_t *uuid,
1036 effect_descriptor_t *descriptor) {
1037 int i;
1038
1039 if (lib_init() != 0)
1040 return init_status;
1041
1042 if (descriptor == NULL || uuid == NULL) {
1043 ALOGV("%s called with NULL pointer", __func__);
1044 return -EINVAL;
1045 }
1046
1047 for (i = 0; descriptors[i] != NULL; i++) {
1048 if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
1049 *descriptor = *descriptors[i];
1050 return 0;
1051 }
1052 }
1053
1054 return -EINVAL;
1055}
1056
1057/*
1058 * Effect Control Interface Implementation
1059 */
1060
1061 /* Stub function for effect interface: never called for offloaded effects */
1062int effect_process(effect_handle_t self,
Haynes Mathew Georgecc9649b2014-06-10 15:08:39 -07001063 audio_buffer_t *inBuffer __unused,
1064 audio_buffer_t *outBuffer __unused)
Eric Laurentc4aef752013-09-12 17:45:53 -07001065{
1066 effect_context_t * context = (effect_context_t *)self;
1067 int status = 0;
1068
1069 ALOGW("%s Called ?????", __func__);
1070
1071 pthread_mutex_lock(&lock);
1072 if (!effect_exists(context)) {
1073 status = -EINVAL;
1074 goto exit;
1075 }
1076
1077 if (context->state != EFFECT_STATE_ACTIVE) {
1078 status = -EINVAL;
1079 goto exit;
1080 }
1081
1082exit:
1083 pthread_mutex_unlock(&lock);
1084 return status;
1085}
1086
1087int effect_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
1088 void *pCmdData, uint32_t *replySize, void *pReplyData)
1089{
1090
1091 effect_context_t * context = (effect_context_t *)self;
1092 int retsize;
1093 int status = 0;
1094
1095 pthread_mutex_lock(&lock);
1096
1097 if (!effect_exists(context)) {
1098 status = -EINVAL;
1099 goto exit;
1100 }
1101
1102 if (context == NULL || context->state == EFFECT_STATE_UNINITIALIZED) {
1103 status = -EINVAL;
1104 goto exit;
1105 }
1106
1107// ALOGV_IF(cmdCode != VISUALIZER_CMD_CAPTURE,
1108// "%s command %d cmdSize %d", __func__, cmdCode, cmdSize);
1109
1110 switch (cmdCode) {
1111 case EFFECT_CMD_INIT:
1112 if (pReplyData == NULL || *replySize != sizeof(int)) {
1113 status = -EINVAL;
1114 goto exit;
1115 }
1116 if (context->ops.init)
1117 *(int *) pReplyData = context->ops.init(context);
1118 else
1119 *(int *) pReplyData = 0;
1120 break;
1121 case EFFECT_CMD_SET_CONFIG:
1122 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
1123 || pReplyData == NULL || *replySize != sizeof(int)) {
1124 status = -EINVAL;
1125 goto exit;
1126 }
1127 *(int *) pReplyData = set_config(context, (effect_config_t *) pCmdData);
1128 break;
1129 case EFFECT_CMD_GET_CONFIG:
1130 if (pReplyData == NULL ||
1131 *replySize != sizeof(effect_config_t)) {
1132 status = -EINVAL;
1133 goto exit;
1134 }
1135 if (!context->offload_enabled) {
1136 status = -EINVAL;
1137 goto exit;
1138 }
1139
1140 get_config(context, (effect_config_t *)pReplyData);
1141 break;
1142 case EFFECT_CMD_RESET:
1143 if (context->ops.reset)
1144 context->ops.reset(context);
1145 break;
1146 case EFFECT_CMD_ENABLE:
1147 if (pReplyData == NULL || *replySize != sizeof(int)) {
1148 status = -EINVAL;
1149 goto exit;
1150 }
1151 if (context->state != EFFECT_STATE_INITIALIZED) {
1152 status = -ENOSYS;
1153 goto exit;
1154 }
1155 context->state = EFFECT_STATE_ACTIVE;
1156 if (context->ops.enable)
1157 context->ops.enable(context);
1158 pthread_cond_signal(&cond);
1159 ALOGV("%s EFFECT_CMD_ENABLE", __func__);
1160 *(int *)pReplyData = 0;
1161 break;
1162 case EFFECT_CMD_DISABLE:
1163 if (pReplyData == NULL || *replySize != sizeof(int)) {
1164 status = -EINVAL;
1165 goto exit;
1166 }
1167 if (context->state != EFFECT_STATE_ACTIVE) {
1168 status = -ENOSYS;
1169 goto exit;
1170 }
1171 context->state = EFFECT_STATE_INITIALIZED;
1172 if (context->ops.disable)
1173 context->ops.disable(context);
1174 pthread_cond_signal(&cond);
1175 ALOGV("%s EFFECT_CMD_DISABLE", __func__);
1176 *(int *)pReplyData = 0;
1177 break;
1178 case EFFECT_CMD_GET_PARAM: {
1179 if (pCmdData == NULL ||
1180 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
1181 pReplyData == NULL ||
1182 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
1183 status = -EINVAL;
1184 goto exit;
1185 }
1186 if (!context->offload_enabled) {
1187 status = -EINVAL;
1188 goto exit;
1189 }
1190 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
1191 effect_param_t *p = (effect_param_t *)pReplyData;
1192 if (context->ops.get_parameter)
1193 context->ops.get_parameter(context, p, replySize);
1194 } break;
1195 case EFFECT_CMD_SET_PARAM: {
1196 if (pCmdData == NULL ||
1197 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
1198 pReplyData == NULL || *replySize != sizeof(int32_t)) {
1199 status = -EINVAL;
1200 goto exit;
1201 }
1202 *(int32_t *)pReplyData = 0;
1203 effect_param_t *p = (effect_param_t *)pCmdData;
1204 if (context->ops.set_parameter)
1205 *(int32_t *)pReplyData = context->ops.set_parameter(context, p, *replySize);
1206
1207 } break;
1208 case EFFECT_CMD_SET_DEVICE:
1209 case EFFECT_CMD_SET_VOLUME:
1210 case EFFECT_CMD_SET_AUDIO_MODE:
1211 break;
1212
1213 case EFFECT_CMD_OFFLOAD: {
1214 output_context_t *out_ctxt;
1215
1216 if (cmdSize != sizeof(effect_offload_param_t) || pCmdData == NULL
1217 || pReplyData == NULL || *replySize != sizeof(int)) {
1218 ALOGV("%s EFFECT_CMD_OFFLOAD bad format", __func__);
1219 status = -EINVAL;
1220 break;
1221 }
1222
1223 effect_offload_param_t* offload_param = (effect_offload_param_t*)pCmdData;
1224
1225 ALOGV("%s EFFECT_CMD_OFFLOAD offload %d output %d",
1226 __func__, offload_param->isOffload, offload_param->ioHandle);
1227
1228 *(int *)pReplyData = 0;
1229
1230 context->offload_enabled = offload_param->isOffload;
1231 if (context->out_handle == offload_param->ioHandle)
1232 break;
1233
1234 out_ctxt = get_output(context->out_handle);
1235 if (out_ctxt != NULL)
1236 remove_effect_from_output(out_ctxt, context);
Haynes Mathew George41f86652014-06-17 14:22:15 -07001237
1238 context->out_handle = offload_param->ioHandle;
Eric Laurentc4aef752013-09-12 17:45:53 -07001239 out_ctxt = get_output(offload_param->ioHandle);
1240 if (out_ctxt != NULL)
1241 add_effect_to_output(out_ctxt, context);
1242
Eric Laurentc4aef752013-09-12 17:45:53 -07001243 } break;
1244
1245
1246 default:
1247 if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY && context->ops.command)
1248 status = context->ops.command(context, cmdCode, cmdSize,
1249 pCmdData, replySize, pReplyData);
1250 else {
1251 ALOGW("%s invalid command %d", __func__, cmdCode);
1252 status = -EINVAL;
1253 }
1254 break;
1255 }
1256
1257exit:
1258 pthread_mutex_unlock(&lock);
1259
1260// ALOGV_IF(cmdCode != VISUALIZER_CMD_CAPTURE,"%s DONE", __func__);
1261 return status;
1262}
1263
1264/* Effect Control Interface Implementation: get_descriptor */
1265int effect_get_descriptor(effect_handle_t self,
1266 effect_descriptor_t *descriptor)
1267{
1268 effect_context_t *context = (effect_context_t *)self;
1269
1270 if (!effect_exists(context))
1271 return -EINVAL;
1272
1273 if (descriptor == NULL)
1274 return -EINVAL;
1275
1276 *descriptor = *context->desc;
1277
1278 return 0;
1279}
1280
1281/* effect_handle_t interface implementation for visualizer effect */
1282const struct effect_interface_s effect_interface = {
1283 effect_process,
1284 effect_command,
1285 effect_get_descriptor,
1286 NULL,
1287};
1288
1289__attribute__ ((visibility ("default")))
1290audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
synergy dev19af91c2013-12-17 17:36:30 -08001291 .tag = AUDIO_EFFECT_LIBRARY_TAG,
1292 .version = EFFECT_LIBRARY_API_VERSION,
1293 .name = "Visualizer Library",
1294 .implementor = "The Android Open Source Project",
1295 .create_effect = effect_lib_create,
1296 .release_effect = effect_lib_release,
1297 .get_descriptor = effect_lib_get_descriptor,
Eric Laurentc4aef752013-09-12 17:45:53 -07001298};