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