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