blob: b2f09528a86a37e57fb9c12f3eb7fef0a991fd90 [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>
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -070020#include <math.h>
Eric Laurentc4aef752013-09-12 17:45:53 -070021#include <stdlib.h>
22#include <string.h>
23#include <time.h>
24#include <sys/prctl.h>
Ravi Kumar Alamanda518bcbb2014-11-14 16:51:10 -080025#include <dlfcn.h>
Eric Laurentc4aef752013-09-12 17:45:53 -070026
27#include <cutils/list.h>
28#include <cutils/log.h>
29#include <system/thread_defs.h>
30#include <tinyalsa/asoundlib.h>
31#include <audio_effects/effect_visualizer.h>
32
Ravi Kumar Alamanda518bcbb2014-11-14 16:51:10 -080033#define LIB_ACDB_LOADER "libacdbloader.so"
34#define ACDB_DEV_TYPE_OUT 1
35#define AFE_PROXY_ACDB_ID 45
36
37static void* acdb_handle;
38
39typedef void (*acdb_send_audio_cal_t)(int, int);
40
41acdb_send_audio_cal_t acdb_send_audio_cal;
Eric Laurentc4aef752013-09-12 17:45:53 -070042
43enum {
44 EFFECT_STATE_UNINITIALIZED,
45 EFFECT_STATE_INITIALIZED,
46 EFFECT_STATE_ACTIVE,
47};
48
49typedef struct effect_context_s effect_context_t;
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -080050typedef struct output_context_s output_context_t;
Eric Laurentc4aef752013-09-12 17:45:53 -070051
52/* effect specific operations. Only the init() and process() operations must be defined.
53 * Others are optional.
54 */
55typedef struct effect_ops_s {
56 int (*init)(effect_context_t *context);
57 int (*release)(effect_context_t *context);
58 int (*reset)(effect_context_t *context);
59 int (*enable)(effect_context_t *context);
60 int (*disable)(effect_context_t *context);
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -080061 int (*start)(effect_context_t *context, output_context_t *output);
62 int (*stop)(effect_context_t *context, output_context_t *output);
Eric Laurentc4aef752013-09-12 17:45:53 -070063 int (*process)(effect_context_t *context, audio_buffer_t *in, audio_buffer_t *out);
64 int (*set_parameter)(effect_context_t *context, effect_param_t *param, uint32_t size);
65 int (*get_parameter)(effect_context_t *context, effect_param_t *param, uint32_t *size);
66 int (*command)(effect_context_t *context, uint32_t cmdCode, uint32_t cmdSize,
67 void *pCmdData, uint32_t *replySize, void *pReplyData);
68} effect_ops_t;
69
70struct effect_context_s {
71 const struct effect_interface_s *itfe;
72 struct listnode effects_list_node; /* node in created_effects_list */
73 struct listnode output_node; /* node in output_context_t.effects_list */
74 effect_config_t config;
75 const effect_descriptor_t *desc;
76 audio_io_handle_t out_handle; /* io handle of the output the effect is attached to */
77 uint32_t state;
78 bool offload_enabled; /* when offload is enabled we process VISUALIZER_CMD_CAPTURE command.
79 Otherwise non offloaded visualizer has already processed the command
80 and we must not overwrite the reply. */
81 effect_ops_t ops;
82};
83
84typedef struct output_context_s {
85 struct listnode outputs_list_node; /* node in active_outputs_list */
86 audio_io_handle_t handle; /* io handle */
87 struct listnode effects_list; /* list of effects attached to this output */
88} output_context_t;
89
90
91/* maximum time since last capture buffer update before resetting capture buffer. This means
92 that the framework has stopped playing audio and we must start returning silence */
93#define MAX_STALL_TIME_MS 1000
94
95#define CAPTURE_BUF_SIZE 65536 /* "64k should be enough for everyone" */
96
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -070097#define DISCARD_MEASUREMENTS_TIME_MS 2000 /* discard measurements older than this number of ms */
98
99/* maximum number of buffers for which we keep track of the measurements */
100#define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25 /* note: buffer index is stored in uint8_t */
101
102typedef struct buffer_stats_s {
103 bool is_valid;
104 uint16_t peak_u16; /* the positive peak of the absolute value of the samples in a buffer */
105 float rms_squared; /* the average square of the samples in a buffer */
106} buffer_stats_t;
Eric Laurentc4aef752013-09-12 17:45:53 -0700107
108typedef struct visualizer_context_s {
109 effect_context_t common;
110
111 uint32_t capture_idx;
112 uint32_t capture_size;
113 uint32_t scaling_mode;
114 uint32_t last_capture_idx;
115 uint32_t latency;
116 struct timespec buffer_update_time;
117 uint8_t capture_buf[CAPTURE_BUF_SIZE];
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700118 /* for measurements */
119 uint8_t channel_count; /* to avoid recomputing it every time a buffer is processed */
120 uint32_t meas_mode;
121 uint8_t meas_wndw_size_in_buffers;
122 uint8_t meas_buffer_idx;
123 buffer_stats_t past_meas[MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS];
Eric Laurentc4aef752013-09-12 17:45:53 -0700124} visualizer_context_t;
125
126
127extern const struct effect_interface_s effect_interface;
128
129/* Offload visualizer UUID: 7a8044a0-1a71-11e3-a184-0002a5d5c51b */
130const effect_descriptor_t visualizer_descriptor = {
131 {0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
132 {0x7a8044a0, 0x1a71, 0x11e3, 0xa184, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
133 EFFECT_CONTROL_API_VERSION,
134 (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_HW_ACC_TUNNEL ),
135 0, /* TODO */
136 1,
137 "QCOM MSM offload visualizer",
138 "The Android Open Source Project",
139};
140
141const effect_descriptor_t *descriptors[] = {
142 &visualizer_descriptor,
143 NULL,
144};
145
146
147pthread_once_t once = PTHREAD_ONCE_INIT;
148int init_status;
149
150/* list of created effects. Updated by visualizer_hal_start_output()
151 * and visualizer_hal_stop_output() */
152struct listnode created_effects_list;
153/* list of active output streams. Updated by visualizer_hal_start_output()
154 * and visualizer_hal_stop_output() */
155struct listnode active_outputs_list;
156
157/* thread capturing PCM from Proxy port and calling the process function on each enabled effect
158 * attached to an active output stream */
159pthread_t capture_thread;
160/* lock must be held when modifying or accessing created_effects_list or active_outputs_list */
161pthread_mutex_t lock;
162/* thread_lock must be held when starting or stopping the capture thread.
163 * Locking order: thread_lock -> lock */
164pthread_mutex_t thread_lock;
165/* cond is signaled when an output is started or stopped or an effect is enabled or disable: the
166 * capture thread will reevaluate the capture and effect rocess conditions. */
167pthread_cond_t cond;
168/* true when requesting the capture thread to exit */
169bool exit_thread;
170/* 0 if the capture thread was created successfully */
171int thread_status;
172
173
174#define DSP_OUTPUT_LATENCY_MS 0 /* Fudge factor for latency after capture point in audio DSP */
175
176/* Retry for delay for mixer open */
177#define RETRY_NUMBER 10
178#define RETRY_US 500000
179
180#define MIXER_CARD 0
181#define SOUND_CARD 0
182#define CAPTURE_DEVICE 8
183
184/* Proxy port supports only MMAP read and those fixed parameters*/
185#define AUDIO_CAPTURE_CHANNEL_COUNT 2
186#define AUDIO_CAPTURE_SMP_RATE 48000
187#define AUDIO_CAPTURE_PERIOD_SIZE (768)
188#define AUDIO_CAPTURE_PERIOD_COUNT 32
189
190struct pcm_config pcm_config_capture = {
191 .channels = AUDIO_CAPTURE_CHANNEL_COUNT,
192 .rate = AUDIO_CAPTURE_SMP_RATE,
193 .period_size = AUDIO_CAPTURE_PERIOD_SIZE,
194 .period_count = AUDIO_CAPTURE_PERIOD_COUNT,
195 .format = PCM_FORMAT_S16_LE,
196 .start_threshold = AUDIO_CAPTURE_PERIOD_SIZE / 4,
197 .stop_threshold = INT_MAX,
198 .avail_min = AUDIO_CAPTURE_PERIOD_SIZE / 4,
199};
200
201
202/*
203 * Local functions
204 */
205
206static void init_once() {
207 list_init(&created_effects_list);
208 list_init(&active_outputs_list);
209
210 pthread_mutex_init(&lock, NULL);
211 pthread_mutex_init(&thread_lock, NULL);
212 pthread_cond_init(&cond, NULL);
213 exit_thread = false;
214 thread_status = -1;
215
216 init_status = 0;
217}
218
219int lib_init() {
220 pthread_once(&once, init_once);
221 return init_status;
222}
223
224bool effect_exists(effect_context_t *context) {
225 struct listnode *node;
226
227 list_for_each(node, &created_effects_list) {
228 effect_context_t *fx_ctxt = node_to_item(node,
229 effect_context_t,
230 effects_list_node);
231 if (fx_ctxt == context) {
232 return true;
233 }
234 }
235 return false;
236}
237
238output_context_t *get_output(audio_io_handle_t output) {
239 struct listnode *node;
240
241 list_for_each(node, &active_outputs_list) {
242 output_context_t *out_ctxt = node_to_item(node,
243 output_context_t,
244 outputs_list_node);
245 if (out_ctxt->handle == output) {
246 return out_ctxt;
247 }
248 }
249 return NULL;
250}
251
252void add_effect_to_output(output_context_t * output, effect_context_t *context) {
253 struct listnode *fx_node;
254
255 list_for_each(fx_node, &output->effects_list) {
256 effect_context_t *fx_ctxt = node_to_item(fx_node,
257 effect_context_t,
258 output_node);
259 if (fx_ctxt == context)
260 return;
261 }
262 list_add_tail(&output->effects_list, &context->output_node);
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -0800263 if (context->ops.start)
264 context->ops.start(context, output);
Eric Laurentc4aef752013-09-12 17:45:53 -0700265}
266
267void remove_effect_from_output(output_context_t * output, effect_context_t *context) {
268 struct listnode *fx_node;
269
270 list_for_each(fx_node, &output->effects_list) {
271 effect_context_t *fx_ctxt = node_to_item(fx_node,
272 effect_context_t,
273 output_node);
274 if (fx_ctxt == context) {
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -0800275 if (context->ops.stop)
276 context->ops.stop(context, output);
Eric Laurentc4aef752013-09-12 17:45:53 -0700277 list_remove(&context->output_node);
278 return;
279 }
280 }
281}
282
283bool effects_enabled() {
284 struct listnode *out_node;
285
286 list_for_each(out_node, &active_outputs_list) {
287 struct listnode *fx_node;
288 output_context_t *out_ctxt = node_to_item(out_node,
289 output_context_t,
290 outputs_list_node);
291
292 list_for_each(fx_node, &out_ctxt->effects_list) {
293 effect_context_t *fx_ctxt = node_to_item(fx_node,
294 effect_context_t,
295 output_node);
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -0800296 if (fx_ctxt->state == EFFECT_STATE_ACTIVE && fx_ctxt->ops.process != NULL)
Eric Laurentc4aef752013-09-12 17:45:53 -0700297 return true;
298 }
299 }
300 return false;
301}
302
vivek mehta39cfce62015-09-18 10:39:16 -0700303int set_control(const char* name, struct mixer *mixer, int value) {
Eric Laurentc4aef752013-09-12 17:45:53 -0700304 struct mixer_ctl *ctl;
305
vivek mehta39cfce62015-09-18 10:39:16 -0700306 ctl = mixer_get_ctl_by_name(mixer, name);
307 if (ctl == NULL) {
308 ALOGW("%s: could not get %s ctl", __func__, name);
309 return -EINVAL;
310 }
311 if (mixer_ctl_set_value(ctl, 0, value) != 0) {
312 ALOGW("%s: error setting value %d on %s ", __func__, value, name);
313 return -EINVAL;
314 }
315
316 return 0;
317}
318
319int configure_proxy_capture(struct mixer *mixer, int value) {
320 int retval = 0;
321
Ravi Kumar Alamanda518bcbb2014-11-14 16:51:10 -0800322 if (value && acdb_send_audio_cal)
323 acdb_send_audio_cal(AFE_PROXY_ACDB_ID, ACDB_DEV_TYPE_OUT);
324
vivek mehta39cfce62015-09-18 10:39:16 -0700325 retval = set_control("AFE_PCM_RX Audio Mixer MultiMedia4", mixer, value);
326
327 if (retval != 0)
328 return retval;
329
330 // Extending visualizer to capture for compress2 path as well.
331 // for extending it to multiple offload either this needs to be extended
332 // or need to find better solution to enable only active offload sessions
333
334 retval = set_control("AFE_PCM_RX Audio Mixer MultiMedia7", mixer, value);
335 if (retval != 0)
336 return retval;
Eric Laurentc4aef752013-09-12 17:45:53 -0700337
338 return 0;
339}
340
341
342void *capture_thread_loop(void *arg)
343{
344 int16_t data[AUDIO_CAPTURE_PERIOD_SIZE * AUDIO_CAPTURE_CHANNEL_COUNT * sizeof(int16_t)];
345 audio_buffer_t buf;
346 buf.frameCount = AUDIO_CAPTURE_PERIOD_SIZE;
347 buf.s16 = data;
348 bool capture_enabled = false;
349 struct mixer *mixer;
350 struct pcm *pcm = NULL;
351 int ret;
352 int retry_num = 0;
353
354 ALOGD("thread enter");
355
356 prctl(PR_SET_NAME, (unsigned long)"visualizer capture", 0, 0, 0);
357
358 pthread_mutex_lock(&lock);
359
360 mixer = mixer_open(MIXER_CARD);
361 while (mixer == NULL && retry_num < RETRY_NUMBER) {
362 usleep(RETRY_US);
363 mixer = mixer_open(MIXER_CARD);
364 retry_num++;
365 }
366 if (mixer == NULL) {
367 pthread_mutex_unlock(&lock);
368 return NULL;
369 }
370
371 for (;;) {
372 if (exit_thread) {
373 break;
374 }
375 if (effects_enabled()) {
376 if (!capture_enabled) {
377 ret = configure_proxy_capture(mixer, 1);
378 if (ret == 0) {
379 pcm = pcm_open(SOUND_CARD, CAPTURE_DEVICE,
380 PCM_IN|PCM_MMAP|PCM_NOIRQ, &pcm_config_capture);
381 if (pcm && !pcm_is_ready(pcm)) {
382 ALOGW("%s: %s", __func__, pcm_get_error(pcm));
383 pcm_close(pcm);
384 pcm = NULL;
385 configure_proxy_capture(mixer, 0);
386 } else {
387 capture_enabled = true;
388 ALOGD("%s: capture ENABLED", __func__);
389 }
390 }
391 }
392 } else {
393 if (capture_enabled) {
394 if (pcm != NULL)
395 pcm_close(pcm);
396 configure_proxy_capture(mixer, 0);
397 ALOGD("%s: capture DISABLED", __func__);
398 capture_enabled = false;
399 }
400 pthread_cond_wait(&cond, &lock);
401 }
402 if (!capture_enabled)
403 continue;
404
405 pthread_mutex_unlock(&lock);
406 ret = pcm_mmap_read(pcm, data, sizeof(data));
407 pthread_mutex_lock(&lock);
408
409 if (ret == 0) {
410 struct listnode *out_node;
411
412 list_for_each(out_node, &active_outputs_list) {
413 output_context_t *out_ctxt = node_to_item(out_node,
414 output_context_t,
415 outputs_list_node);
416 struct listnode *fx_node;
417
418 list_for_each(fx_node, &out_ctxt->effects_list) {
419 effect_context_t *fx_ctxt = node_to_item(fx_node,
420 effect_context_t,
421 output_node);
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -0800422 if (fx_ctxt->ops.process != NULL)
423 fx_ctxt->ops.process(fx_ctxt, &buf, &buf);
Eric Laurentc4aef752013-09-12 17:45:53 -0700424 }
425 }
426 } else {
427 ALOGW("%s: read status %d %s", __func__, ret, pcm_get_error(pcm));
428 }
429 }
430
431 if (capture_enabled) {
432 if (pcm != NULL)
433 pcm_close(pcm);
434 configure_proxy_capture(mixer, 0);
435 }
436 mixer_close(mixer);
437 pthread_mutex_unlock(&lock);
438
439 ALOGD("thread exit");
440
441 return NULL;
442}
443
444/*
445 * Interface from audio HAL
446 */
447
448__attribute__ ((visibility ("default")))
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -0800449int visualizer_hal_start_output(audio_io_handle_t output, int pcm_id) {
Eric Laurentc4aef752013-09-12 17:45:53 -0700450 int ret;
451 struct listnode *node;
452
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -0800453 ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
Eric Laurentc4aef752013-09-12 17:45:53 -0700454
455 if (lib_init() != 0)
456 return init_status;
457
458 pthread_mutex_lock(&thread_lock);
459 pthread_mutex_lock(&lock);
460 if (get_output(output) != NULL) {
461 ALOGW("%s output already started", __func__);
462 ret = -ENOSYS;
463 goto exit;
464 }
465
466 output_context_t *out_ctxt = (output_context_t *)malloc(sizeof(output_context_t));
wjiangebb69fa2014-05-15 19:38:26 +0800467 if (out_ctxt == NULL) {
468 ALOGE("%s fail to allocate memory", __func__);
469 ret = -ENOMEM;
470 goto exit;
471 }
Eric Laurentc4aef752013-09-12 17:45:53 -0700472 out_ctxt->handle = output;
473 list_init(&out_ctxt->effects_list);
474
475 list_for_each(node, &created_effects_list) {
476 effect_context_t *fx_ctxt = node_to_item(node,
477 effect_context_t,
478 effects_list_node);
479 if (fx_ctxt->out_handle == output) {
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -0800480 if (fx_ctxt->ops.start)
481 fx_ctxt->ops.start(fx_ctxt, out_ctxt);
Eric Laurentc4aef752013-09-12 17:45:53 -0700482 list_add_tail(&out_ctxt->effects_list, &fx_ctxt->output_node);
483 }
484 }
485 if (list_empty(&active_outputs_list)) {
486 exit_thread = false;
487 thread_status = pthread_create(&capture_thread, (const pthread_attr_t *) NULL,
488 capture_thread_loop, NULL);
489 }
490 list_add_tail(&active_outputs_list, &out_ctxt->outputs_list_node);
491 pthread_cond_signal(&cond);
492
493exit:
494 pthread_mutex_unlock(&lock);
495 pthread_mutex_unlock(&thread_lock);
496 return ret;
497}
498
499__attribute__ ((visibility ("default")))
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -0800500int visualizer_hal_stop_output(audio_io_handle_t output, int pcm_id) {
Eric Laurentc4aef752013-09-12 17:45:53 -0700501 int ret;
502 struct listnode *node;
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -0800503 struct listnode *fx_node;
Eric Laurentc4aef752013-09-12 17:45:53 -0700504 output_context_t *out_ctxt;
505
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -0800506 ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
Eric Laurentc4aef752013-09-12 17:45:53 -0700507
508 if (lib_init() != 0)
509 return init_status;
510
511 pthread_mutex_lock(&thread_lock);
512 pthread_mutex_lock(&lock);
513
514 out_ctxt = get_output(output);
515 if (out_ctxt == NULL) {
516 ALOGW("%s output not started", __func__);
517 ret = -ENOSYS;
518 goto exit;
519 }
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -0800520 list_for_each(fx_node, &out_ctxt->effects_list) {
521 effect_context_t *fx_ctxt = node_to_item(fx_node,
522 effect_context_t,
523 output_node);
524 if (fx_ctxt->ops.stop)
525 fx_ctxt->ops.stop(fx_ctxt, out_ctxt);
526 }
Eric Laurentc4aef752013-09-12 17:45:53 -0700527 list_remove(&out_ctxt->outputs_list_node);
528 pthread_cond_signal(&cond);
529
530 if (list_empty(&active_outputs_list)) {
531 if (thread_status == 0) {
532 exit_thread = true;
533 pthread_cond_signal(&cond);
534 pthread_mutex_unlock(&lock);
535 pthread_join(capture_thread, (void **) NULL);
536 pthread_mutex_lock(&lock);
537 thread_status = -1;
538 }
539 }
540
541 free(out_ctxt);
542
543exit:
544 pthread_mutex_unlock(&lock);
545 pthread_mutex_unlock(&thread_lock);
546 return ret;
547}
548
549
550/*
551 * Effect operations
552 */
553
554int set_config(effect_context_t *context, effect_config_t *config)
555{
556 if (config->inputCfg.samplingRate != config->outputCfg.samplingRate) return -EINVAL;
557 if (config->inputCfg.channels != config->outputCfg.channels) return -EINVAL;
558 if (config->inputCfg.format != config->outputCfg.format) return -EINVAL;
559 if (config->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
560 if (config->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
561 config->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
562 if (config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL;
563
564 context->config = *config;
565
566 if (context->ops.reset)
567 context->ops.reset(context);
568
569 return 0;
570}
571
572void get_config(effect_context_t *context, effect_config_t *config)
573{
574 *config = context->config;
575}
576
577
578/*
579 * Visualizer operations
580 */
581
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700582uint32_t visualizer_get_delta_time_ms_from_updated_time(visualizer_context_t* visu_ctxt) {
583 uint32_t delta_ms = 0;
584 if (visu_ctxt->buffer_update_time.tv_sec != 0) {
585 struct timespec ts;
586 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
587 time_t secs = ts.tv_sec - visu_ctxt->buffer_update_time.tv_sec;
588 long nsec = ts.tv_nsec - visu_ctxt->buffer_update_time.tv_nsec;
589 if (nsec < 0) {
590 --secs;
591 nsec += 1000000000;
592 }
593 delta_ms = secs * 1000 + nsec / 1000000;
594 }
595 }
596 return delta_ms;
597}
598
Eric Laurentc4aef752013-09-12 17:45:53 -0700599int visualizer_reset(effect_context_t *context)
600{
601 visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
602
603 visu_ctxt->capture_idx = 0;
604 visu_ctxt->last_capture_idx = 0;
605 visu_ctxt->buffer_update_time.tv_sec = 0;
606 visu_ctxt->latency = DSP_OUTPUT_LATENCY_MS;
607 memset(visu_ctxt->capture_buf, 0x80, CAPTURE_BUF_SIZE);
608 return 0;
609}
610
611int visualizer_init(effect_context_t *context)
612{
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700613 int32_t i;
614
Eric Laurentc4aef752013-09-12 17:45:53 -0700615 visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
616
617 context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
618 context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
619 context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
620 context->config.inputCfg.samplingRate = 44100;
621 context->config.inputCfg.bufferProvider.getBuffer = NULL;
622 context->config.inputCfg.bufferProvider.releaseBuffer = NULL;
623 context->config.inputCfg.bufferProvider.cookie = NULL;
624 context->config.inputCfg.mask = EFFECT_CONFIG_ALL;
625 context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
626 context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
627 context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
628 context->config.outputCfg.samplingRate = 44100;
629 context->config.outputCfg.bufferProvider.getBuffer = NULL;
630 context->config.outputCfg.bufferProvider.releaseBuffer = NULL;
631 context->config.outputCfg.bufferProvider.cookie = NULL;
632 context->config.outputCfg.mask = EFFECT_CONFIG_ALL;
633
634 visu_ctxt->capture_size = VISUALIZER_CAPTURE_SIZE_MAX;
635 visu_ctxt->scaling_mode = VISUALIZER_SCALING_MODE_NORMALIZED;
636
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700637 // measurement initialization
638 visu_ctxt->channel_count = popcount(context->config.inputCfg.channels);
639 visu_ctxt->meas_mode = MEASUREMENT_MODE_NONE;
640 visu_ctxt->meas_wndw_size_in_buffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS;
641 visu_ctxt->meas_buffer_idx = 0;
642 for (i=0 ; i<visu_ctxt->meas_wndw_size_in_buffers ; i++) {
643 visu_ctxt->past_meas[i].is_valid = false;
644 visu_ctxt->past_meas[i].peak_u16 = 0;
645 visu_ctxt->past_meas[i].rms_squared = 0;
646 }
647
Eric Laurentc4aef752013-09-12 17:45:53 -0700648 set_config(context, &context->config);
649
Ravi Kumar Alamanda518bcbb2014-11-14 16:51:10 -0800650 if (acdb_handle == NULL) {
651 acdb_handle = dlopen(LIB_ACDB_LOADER, RTLD_NOW);
652 if (acdb_handle == NULL) {
653 ALOGE("%s: DLOPEN failed for %s", __func__, LIB_ACDB_LOADER);
654 } else {
655 acdb_send_audio_cal = (acdb_send_audio_cal_t)dlsym(acdb_handle,
656 "acdb_loader_send_audio_cal");
657 if (!acdb_send_audio_cal)
658 ALOGE("%s: Could not find the symbol acdb_send_audio_cal from %s",
659 __func__, LIB_ACDB_LOADER);
660 }
661 }
662
Eric Laurentc4aef752013-09-12 17:45:53 -0700663 return 0;
664}
665
666int visualizer_get_parameter(effect_context_t *context, effect_param_t *p, uint32_t *size)
667{
668 visualizer_context_t *visu_ctxt = (visualizer_context_t *)context;
669
670 p->status = 0;
671 *size = sizeof(effect_param_t) + sizeof(uint32_t);
672 if (p->psize != sizeof(uint32_t)) {
673 p->status = -EINVAL;
674 return 0;
675 }
676 switch (*(uint32_t *)p->data) {
677 case VISUALIZER_PARAM_CAPTURE_SIZE:
678 ALOGV("%s get capture_size = %d", __func__, visu_ctxt->capture_size);
679 *((uint32_t *)p->data + 1) = visu_ctxt->capture_size;
680 p->vsize = sizeof(uint32_t);
681 *size += sizeof(uint32_t);
682 break;
683 case VISUALIZER_PARAM_SCALING_MODE:
684 ALOGV("%s get scaling_mode = %d", __func__, visu_ctxt->scaling_mode);
685 *((uint32_t *)p->data + 1) = visu_ctxt->scaling_mode;
686 p->vsize = sizeof(uint32_t);
687 *size += sizeof(uint32_t);
688 break;
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700689 case VISUALIZER_PARAM_MEASUREMENT_MODE:
690 ALOGV("%s get meas_mode = %d", __func__, visu_ctxt->meas_mode);
691 *((uint32_t *)p->data + 1) = visu_ctxt->meas_mode;
692 p->vsize = sizeof(uint32_t);
693 *size += sizeof(uint32_t);
694 break;
Eric Laurentc4aef752013-09-12 17:45:53 -0700695 default:
696 p->status = -EINVAL;
697 }
698 return 0;
699}
700
701int visualizer_set_parameter(effect_context_t *context, effect_param_t *p, uint32_t size)
702{
703 visualizer_context_t *visu_ctxt = (visualizer_context_t *)context;
704
705 if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t))
706 return -EINVAL;
707
708 switch (*(uint32_t *)p->data) {
709 case VISUALIZER_PARAM_CAPTURE_SIZE:
710 visu_ctxt->capture_size = *((uint32_t *)p->data + 1);
711 ALOGV("%s set capture_size = %d", __func__, visu_ctxt->capture_size);
712 break;
713 case VISUALIZER_PARAM_SCALING_MODE:
714 visu_ctxt->scaling_mode = *((uint32_t *)p->data + 1);
715 ALOGV("%s set scaling_mode = %d", __func__, visu_ctxt->scaling_mode);
716 break;
717 case VISUALIZER_PARAM_LATENCY:
718 /* Ignore latency as we capture at DSP output
719 * visu_ctxt->latency = *((uint32_t *)p->data + 1); */
720 ALOGV("%s set latency = %d", __func__, visu_ctxt->latency);
721 break;
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700722 case VISUALIZER_PARAM_MEASUREMENT_MODE:
723 visu_ctxt->meas_mode = *((uint32_t *)p->data + 1);
724 ALOGV("%s set meas_mode = %d", __func__, visu_ctxt->meas_mode);
725 break;
Eric Laurentc4aef752013-09-12 17:45:53 -0700726 default:
727 return -EINVAL;
728 }
729 return 0;
730}
731
732/* Real process function called from capture thread. Called with lock held */
733int visualizer_process(effect_context_t *context,
734 audio_buffer_t *inBuffer,
735 audio_buffer_t *outBuffer)
736{
737 visualizer_context_t *visu_ctxt = (visualizer_context_t *)context;
738
739 if (!effect_exists(context))
740 return -EINVAL;
741
742 if (inBuffer == NULL || inBuffer->raw == NULL ||
743 outBuffer == NULL || outBuffer->raw == NULL ||
744 inBuffer->frameCount != outBuffer->frameCount ||
745 inBuffer->frameCount == 0) {
746 return -EINVAL;
747 }
748
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700749 // perform measurements if needed
750 if (visu_ctxt->meas_mode & MEASUREMENT_MODE_PEAK_RMS) {
751 // find the peak and RMS squared for the new buffer
752 uint32_t inIdx;
753 int16_t max_sample = 0;
754 float rms_squared_acc = 0;
755 for (inIdx = 0 ; inIdx < inBuffer->frameCount * visu_ctxt->channel_count ; inIdx++) {
756 if (inBuffer->s16[inIdx] > max_sample) {
757 max_sample = inBuffer->s16[inIdx];
758 } else if (-inBuffer->s16[inIdx] > max_sample) {
759 max_sample = -inBuffer->s16[inIdx];
760 }
761 rms_squared_acc += (inBuffer->s16[inIdx] * inBuffer->s16[inIdx]);
762 }
763 // store the measurement
764 visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].peak_u16 = (uint16_t)max_sample;
765 visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].rms_squared =
766 rms_squared_acc / (inBuffer->frameCount * visu_ctxt->channel_count);
767 visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].is_valid = true;
768 if (++visu_ctxt->meas_buffer_idx >= visu_ctxt->meas_wndw_size_in_buffers) {
769 visu_ctxt->meas_buffer_idx = 0;
770 }
771 }
772
Eric Laurentc4aef752013-09-12 17:45:53 -0700773 /* all code below assumes stereo 16 bit PCM output and input */
774 int32_t shift;
775
776 if (visu_ctxt->scaling_mode == VISUALIZER_SCALING_MODE_NORMALIZED) {
777 /* derive capture scaling factor from peak value in current buffer
778 * this gives more interesting captures for display. */
779 shift = 32;
780 int len = inBuffer->frameCount * 2;
781 int i;
782 for (i = 0; i < len; i++) {
783 int32_t smp = inBuffer->s16[i];
784 if (smp < 0) smp = -smp - 1; /* take care to keep the max negative in range */
785 int32_t clz = __builtin_clz(smp);
786 if (shift > clz) shift = clz;
787 }
788 /* A maximum amplitude signal will have 17 leading zeros, which we want to
789 * translate to a shift of 8 (for converting 16 bit to 8 bit) */
790 shift = 25 - shift;
791 /* Never scale by less than 8 to avoid returning unaltered PCM signal. */
792 if (shift < 3) {
793 shift = 3;
794 }
795 /* add one to combine the division by 2 needed after summing
796 * left and right channels below */
797 shift++;
798 } else {
799 assert(visu_ctxt->scaling_mode == VISUALIZER_SCALING_MODE_AS_PLAYED);
800 shift = 9;
801 }
802
803 uint32_t capt_idx;
804 uint32_t in_idx;
805 uint8_t *buf = visu_ctxt->capture_buf;
806 for (in_idx = 0, capt_idx = visu_ctxt->capture_idx;
807 in_idx < inBuffer->frameCount;
808 in_idx++, capt_idx++) {
809 if (capt_idx >= CAPTURE_BUF_SIZE) {
810 /* wrap around */
811 capt_idx = 0;
812 }
813 int32_t smp = inBuffer->s16[2 * in_idx] + inBuffer->s16[2 * in_idx + 1];
814 smp = smp >> shift;
815 buf[capt_idx] = ((uint8_t)smp)^0x80;
816 }
817
818 /* XXX the following two should really be atomic, though it probably doesn't
819 * matter much for visualization purposes */
820 visu_ctxt->capture_idx = capt_idx;
821 /* update last buffer update time stamp */
822 if (clock_gettime(CLOCK_MONOTONIC, &visu_ctxt->buffer_update_time) < 0) {
823 visu_ctxt->buffer_update_time.tv_sec = 0;
824 }
825
826 if (context->state != EFFECT_STATE_ACTIVE) {
827 ALOGV("%s DONE inactive", __func__);
828 return -ENODATA;
829 }
830
831 return 0;
832}
833
834int visualizer_command(effect_context_t * context, uint32_t cmdCode, uint32_t cmdSize,
835 void *pCmdData, uint32_t *replySize, void *pReplyData)
836{
837 visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
838
839 switch (cmdCode) {
840 case VISUALIZER_CMD_CAPTURE:
841 if (pReplyData == NULL || *replySize != visu_ctxt->capture_size) {
842 ALOGV("%s VISUALIZER_CMD_CAPTURE error *replySize %d context->capture_size %d",
843 __func__, *replySize, visu_ctxt->capture_size);
844 return -EINVAL;
845 }
846
847 if (!context->offload_enabled)
848 break;
849
850 if (context->state == EFFECT_STATE_ACTIVE) {
851 int32_t latency_ms = visu_ctxt->latency;
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700852 const uint32_t delta_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt);
853 latency_ms -= delta_ms;
854 if (latency_ms < 0) {
855 latency_ms = 0;
Eric Laurentc4aef752013-09-12 17:45:53 -0700856 }
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700857 const uint32_t delta_smp = context->config.inputCfg.samplingRate * latency_ms / 1000;
Eric Laurentc4aef752013-09-12 17:45:53 -0700858
859 int32_t capture_point = visu_ctxt->capture_idx - visu_ctxt->capture_size - delta_smp;
860 int32_t capture_size = visu_ctxt->capture_size;
861 if (capture_point < 0) {
862 int32_t size = -capture_point;
863 if (size > capture_size)
864 size = capture_size;
865
866 memcpy(pReplyData,
867 visu_ctxt->capture_buf + CAPTURE_BUF_SIZE + capture_point,
868 size);
869 pReplyData = (void *)((size_t)pReplyData + size);
870 capture_size -= size;
871 capture_point = 0;
872 }
873 memcpy(pReplyData,
874 visu_ctxt->capture_buf + capture_point,
875 capture_size);
876
877
878 /* if audio framework has stopped playing audio although the effect is still
879 * active we must clear the capture buffer to return silence */
880 if ((visu_ctxt->last_capture_idx == visu_ctxt->capture_idx) &&
881 (visu_ctxt->buffer_update_time.tv_sec != 0)) {
882 if (delta_ms > MAX_STALL_TIME_MS) {
883 ALOGV("%s capture going to idle", __func__);
884 visu_ctxt->buffer_update_time.tv_sec = 0;
885 memset(pReplyData, 0x80, visu_ctxt->capture_size);
886 }
887 }
888 visu_ctxt->last_capture_idx = visu_ctxt->capture_idx;
889 } else {
890 memset(pReplyData, 0x80, visu_ctxt->capture_size);
891 }
892 break;
893
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700894 case VISUALIZER_CMD_MEASURE: {
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
Eric Laurentc4aef752013-09-12 17:45:53 -0700944 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
956int effect_lib_create(const effect_uuid_t *uuid,
957 int32_t sessionId,
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));
wjiangebb69fa2014-05-15 19:38:26 +0800981 if (visu_ctxt == NULL) {
982 ALOGE("%s fail to allocate memory", __func__);
983 return -ENOMEM;
984 }
Eric Laurentc4aef752013-09-12 17:45:53 -0700985 context = (effect_context_t *)visu_ctxt;
986 context->ops.init = visualizer_init;
987 context->ops.reset = visualizer_reset;
988 context->ops.process = visualizer_process;
989 context->ops.set_parameter = visualizer_set_parameter;
990 context->ops.get_parameter = visualizer_get_parameter;
991 context->ops.command = visualizer_command;
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -0800992 context->desc = &visualizer_descriptor;
Eric Laurentc4aef752013-09-12 17:45:53 -0700993 } else {
994 return -EINVAL;
995 }
996
997 context->itfe = &effect_interface;
998 context->state = EFFECT_STATE_UNINITIALIZED;
999 context->out_handle = (audio_io_handle_t)ioId;
Eric Laurentc4aef752013-09-12 17:45:53 -07001000
1001 ret = context->ops.init(context);
1002 if (ret < 0) {
1003 ALOGW("%s init failed", __func__);
1004 free(context);
1005 return ret;
1006 }
1007
1008 context->state = EFFECT_STATE_INITIALIZED;
1009
1010 pthread_mutex_lock(&lock);
1011 list_add_tail(&created_effects_list, &context->effects_list_node);
1012 output_context_t *out_ctxt = get_output(ioId);
1013 if (out_ctxt != NULL)
1014 add_effect_to_output(out_ctxt, context);
1015 pthread_mutex_unlock(&lock);
1016
1017 *pHandle = (effect_handle_t)context;
1018
1019 ALOGV("%s created context %p", __func__, context);
1020
1021 return 0;
1022
1023}
1024
1025int effect_lib_release(effect_handle_t handle) {
1026 effect_context_t *context = (effect_context_t *)handle;
1027 int status;
1028
1029 if (lib_init() != 0)
1030 return init_status;
1031
1032 ALOGV("%s context %p", __func__, handle);
1033 pthread_mutex_lock(&lock);
1034 status = -EINVAL;
1035 if (effect_exists(context)) {
1036 output_context_t *out_ctxt = get_output(context->out_handle);
1037 if (out_ctxt != NULL)
1038 remove_effect_from_output(out_ctxt, context);
1039 list_remove(&context->effects_list_node);
1040 if (context->ops.release)
1041 context->ops.release(context);
1042 free(context);
1043 status = 0;
1044 }
1045 pthread_mutex_unlock(&lock);
1046
1047 return status;
1048}
1049
1050int effect_lib_get_descriptor(const effect_uuid_t *uuid,
1051 effect_descriptor_t *descriptor) {
1052 int i;
1053
1054 if (lib_init() != 0)
1055 return init_status;
1056
1057 if (descriptor == NULL || uuid == NULL) {
1058 ALOGV("%s called with NULL pointer", __func__);
1059 return -EINVAL;
1060 }
1061
1062 for (i = 0; descriptors[i] != NULL; i++) {
1063 if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
1064 *descriptor = *descriptors[i];
1065 return 0;
1066 }
1067 }
1068
1069 return -EINVAL;
1070}
1071
1072/*
1073 * Effect Control Interface Implementation
1074 */
1075
1076 /* Stub function for effect interface: never called for offloaded effects */
1077int effect_process(effect_handle_t self,
1078 audio_buffer_t *inBuffer,
1079 audio_buffer_t *outBuffer)
1080{
1081 effect_context_t * context = (effect_context_t *)self;
1082 int status = 0;
1083
1084 ALOGW("%s Called ?????", __func__);
1085
1086 pthread_mutex_lock(&lock);
1087 if (!effect_exists(context)) {
1088 status = -EINVAL;
1089 goto exit;
1090 }
1091
1092 if (context->state != EFFECT_STATE_ACTIVE) {
1093 status = -EINVAL;
1094 goto exit;
1095 }
1096
1097exit:
1098 pthread_mutex_unlock(&lock);
1099 return status;
1100}
1101
1102int effect_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
1103 void *pCmdData, uint32_t *replySize, void *pReplyData)
1104{
1105
1106 effect_context_t * context = (effect_context_t *)self;
1107 int retsize;
1108 int status = 0;
1109
1110 pthread_mutex_lock(&lock);
1111
1112 if (!effect_exists(context)) {
1113 status = -EINVAL;
1114 goto exit;
1115 }
1116
1117 if (context == NULL || context->state == EFFECT_STATE_UNINITIALIZED) {
1118 status = -EINVAL;
1119 goto exit;
1120 }
1121
1122// ALOGV_IF(cmdCode != VISUALIZER_CMD_CAPTURE,
1123// "%s command %d cmdSize %d", __func__, cmdCode, cmdSize);
1124
1125 switch (cmdCode) {
1126 case EFFECT_CMD_INIT:
1127 if (pReplyData == NULL || *replySize != sizeof(int)) {
1128 status = -EINVAL;
1129 goto exit;
1130 }
1131 if (context->ops.init)
1132 *(int *) pReplyData = context->ops.init(context);
1133 else
1134 *(int *) pReplyData = 0;
1135 break;
1136 case EFFECT_CMD_SET_CONFIG:
1137 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
1138 || pReplyData == NULL || *replySize != sizeof(int)) {
1139 status = -EINVAL;
1140 goto exit;
1141 }
1142 *(int *) pReplyData = set_config(context, (effect_config_t *) pCmdData);
1143 break;
1144 case EFFECT_CMD_GET_CONFIG:
1145 if (pReplyData == NULL ||
1146 *replySize != sizeof(effect_config_t)) {
1147 status = -EINVAL;
1148 goto exit;
1149 }
1150 if (!context->offload_enabled) {
1151 status = -EINVAL;
1152 goto exit;
1153 }
1154
1155 get_config(context, (effect_config_t *)pReplyData);
1156 break;
1157 case EFFECT_CMD_RESET:
1158 if (context->ops.reset)
1159 context->ops.reset(context);
1160 break;
1161 case EFFECT_CMD_ENABLE:
1162 if (pReplyData == NULL || *replySize != sizeof(int)) {
1163 status = -EINVAL;
1164 goto exit;
1165 }
1166 if (context->state != EFFECT_STATE_INITIALIZED) {
1167 status = -ENOSYS;
1168 goto exit;
1169 }
1170 context->state = EFFECT_STATE_ACTIVE;
1171 if (context->ops.enable)
1172 context->ops.enable(context);
1173 pthread_cond_signal(&cond);
1174 ALOGV("%s EFFECT_CMD_ENABLE", __func__);
1175 *(int *)pReplyData = 0;
1176 break;
1177 case EFFECT_CMD_DISABLE:
1178 if (pReplyData == NULL || *replySize != sizeof(int)) {
1179 status = -EINVAL;
1180 goto exit;
1181 }
1182 if (context->state != EFFECT_STATE_ACTIVE) {
1183 status = -ENOSYS;
1184 goto exit;
1185 }
1186 context->state = EFFECT_STATE_INITIALIZED;
1187 if (context->ops.disable)
1188 context->ops.disable(context);
1189 pthread_cond_signal(&cond);
1190 ALOGV("%s EFFECT_CMD_DISABLE", __func__);
1191 *(int *)pReplyData = 0;
1192 break;
1193 case EFFECT_CMD_GET_PARAM: {
1194 if (pCmdData == NULL ||
1195 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
1196 pReplyData == NULL ||
1197 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
1198 status = -EINVAL;
1199 goto exit;
1200 }
1201 if (!context->offload_enabled) {
1202 status = -EINVAL;
1203 goto exit;
1204 }
1205 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
1206 effect_param_t *p = (effect_param_t *)pReplyData;
1207 if (context->ops.get_parameter)
1208 context->ops.get_parameter(context, p, replySize);
1209 } break;
1210 case EFFECT_CMD_SET_PARAM: {
1211 if (pCmdData == NULL ||
1212 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
1213 pReplyData == NULL || *replySize != sizeof(int32_t)) {
1214 status = -EINVAL;
1215 goto exit;
1216 }
1217 *(int32_t *)pReplyData = 0;
1218 effect_param_t *p = (effect_param_t *)pCmdData;
1219 if (context->ops.set_parameter)
1220 *(int32_t *)pReplyData = context->ops.set_parameter(context, p, *replySize);
1221
1222 } break;
1223 case EFFECT_CMD_SET_DEVICE:
1224 case EFFECT_CMD_SET_VOLUME:
1225 case EFFECT_CMD_SET_AUDIO_MODE:
1226 break;
1227
1228 case EFFECT_CMD_OFFLOAD: {
1229 output_context_t *out_ctxt;
1230
1231 if (cmdSize != sizeof(effect_offload_param_t) || pCmdData == NULL
1232 || pReplyData == NULL || *replySize != sizeof(int)) {
1233 ALOGV("%s EFFECT_CMD_OFFLOAD bad format", __func__);
1234 status = -EINVAL;
1235 break;
1236 }
1237
1238 effect_offload_param_t* offload_param = (effect_offload_param_t*)pCmdData;
1239
1240 ALOGV("%s EFFECT_CMD_OFFLOAD offload %d output %d",
1241 __func__, offload_param->isOffload, offload_param->ioHandle);
1242
1243 *(int *)pReplyData = 0;
1244
1245 context->offload_enabled = offload_param->isOffload;
1246 if (context->out_handle == offload_param->ioHandle)
1247 break;
1248
1249 out_ctxt = get_output(context->out_handle);
1250 if (out_ctxt != NULL)
1251 remove_effect_from_output(out_ctxt, context);
Subhash Chandra Bose Naripeddy1d089162013-11-13 13:31:50 -08001252
1253 context->out_handle = offload_param->ioHandle;
Eric Laurentc4aef752013-09-12 17:45:53 -07001254 out_ctxt = get_output(offload_param->ioHandle);
1255 if (out_ctxt != NULL)
1256 add_effect_to_output(out_ctxt, context);
1257
Eric Laurentc4aef752013-09-12 17:45:53 -07001258 } break;
1259
1260
1261 default:
1262 if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY && context->ops.command)
1263 status = context->ops.command(context, cmdCode, cmdSize,
1264 pCmdData, replySize, pReplyData);
1265 else {
1266 ALOGW("%s invalid command %d", __func__, cmdCode);
1267 status = -EINVAL;
1268 }
1269 break;
1270 }
1271
1272exit:
1273 pthread_mutex_unlock(&lock);
1274
1275// ALOGV_IF(cmdCode != VISUALIZER_CMD_CAPTURE,"%s DONE", __func__);
1276 return status;
1277}
1278
1279/* Effect Control Interface Implementation: get_descriptor */
1280int effect_get_descriptor(effect_handle_t self,
1281 effect_descriptor_t *descriptor)
1282{
1283 effect_context_t *context = (effect_context_t *)self;
1284
1285 if (!effect_exists(context))
1286 return -EINVAL;
1287
1288 if (descriptor == NULL)
1289 return -EINVAL;
1290
1291 *descriptor = *context->desc;
1292
1293 return 0;
1294}
1295
1296/* effect_handle_t interface implementation for visualizer effect */
1297const struct effect_interface_s effect_interface = {
1298 effect_process,
1299 effect_command,
1300 effect_get_descriptor,
1301 NULL,
1302};
1303
1304__attribute__ ((visibility ("default")))
1305audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
1306 tag : AUDIO_EFFECT_LIBRARY_TAG,
1307 version : EFFECT_LIBRARY_API_VERSION,
1308 name : "Visualizer Library",
1309 implementor : "The Android Open Source Project",
1310 create_effect : effect_lib_create,
1311 release_effect : effect_lib_release,
1312 get_descriptor : effect_lib_get_descriptor,
1313};