blob: 74beac733642d13055633031f52d54b6c5eda0cb [file] [log] [blame]
Eric Laurentc4aef752013-09-12 17:45:53 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "offload_visualizer"
18/*#define LOG_NDEBUG 0*/
19#include <assert.h>
Steven Moreland30faba12017-06-30 15:02:57 -070020#include <dlfcn.h>
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -070021#include <math.h>
Steven Moreland30faba12017-06-30 15:02:57 -070022#include <pthread.h>
Eric Laurentc4aef752013-09-12 17:45:53 -070023#include <stdlib.h>
24#include <string.h>
Eric Laurentc4aef752013-09-12 17:45:53 -070025#include <sys/prctl.h>
Steven Moreland30faba12017-06-30 15:02:57 -070026#include <time.h>
27#include <unistd.h>
Eric Laurentc4aef752013-09-12 17:45:53 -070028
29#include <cutils/list.h>
30#include <cutils/log.h>
31#include <system/thread_defs.h>
32#include <tinyalsa/asoundlib.h>
33#include <audio_effects/effect_visualizer.h>
34
Ravi Kumar Alamanda8652b462014-11-14 16:51:10 -080035#define LIB_ACDB_LOADER "libacdbloader.so"
36#define ACDB_DEV_TYPE_OUT 1
37#define AFE_PROXY_ACDB_ID 45
38
39static void* acdb_handle;
40
41typedef void (*acdb_send_audio_cal_t)(int, int);
42
43acdb_send_audio_cal_t acdb_send_audio_cal;
Eric Laurentc4aef752013-09-12 17:45:53 -070044
45enum {
46 EFFECT_STATE_UNINITIALIZED,
47 EFFECT_STATE_INITIALIZED,
48 EFFECT_STATE_ACTIVE,
49};
50
51typedef struct effect_context_s effect_context_t;
Haynes Mathew George41f86652014-06-17 14:22:15 -070052typedef struct output_context_s output_context_t;
Eric Laurentc4aef752013-09-12 17:45:53 -070053
54/* effect specific operations. Only the init() and process() operations must be defined.
55 * Others are optional.
56 */
57typedef struct effect_ops_s {
58 int (*init)(effect_context_t *context);
59 int (*release)(effect_context_t *context);
60 int (*reset)(effect_context_t *context);
61 int (*enable)(effect_context_t *context);
62 int (*disable)(effect_context_t *context);
Haynes Mathew George41f86652014-06-17 14:22:15 -070063 int (*start)(effect_context_t *context, output_context_t *output);
64 int (*stop)(effect_context_t *context, output_context_t *output);
Eric Laurentc4aef752013-09-12 17:45:53 -070065 int (*process)(effect_context_t *context, audio_buffer_t *in, audio_buffer_t *out);
66 int (*set_parameter)(effect_context_t *context, effect_param_t *param, uint32_t size);
67 int (*get_parameter)(effect_context_t *context, effect_param_t *param, uint32_t *size);
68 int (*command)(effect_context_t *context, uint32_t cmdCode, uint32_t cmdSize,
69 void *pCmdData, uint32_t *replySize, void *pReplyData);
70} effect_ops_t;
71
72struct effect_context_s {
73 const struct effect_interface_s *itfe;
74 struct listnode effects_list_node; /* node in created_effects_list */
75 struct listnode output_node; /* node in output_context_t.effects_list */
76 effect_config_t config;
77 const effect_descriptor_t *desc;
78 audio_io_handle_t out_handle; /* io handle of the output the effect is attached to */
79 uint32_t state;
80 bool offload_enabled; /* when offload is enabled we process VISUALIZER_CMD_CAPTURE command.
81 Otherwise non offloaded visualizer has already processed the command
82 and we must not overwrite the reply. */
83 effect_ops_t ops;
84};
85
David Benjaminc9d8d892016-09-21 12:04:55 -040086struct output_context_s {
Eric Laurentc4aef752013-09-12 17:45:53 -070087 struct listnode outputs_list_node; /* node in active_outputs_list */
88 audio_io_handle_t handle; /* io handle */
89 struct listnode effects_list; /* list of effects attached to this output */
David Benjaminc9d8d892016-09-21 12:04:55 -040090};
Eric Laurentc4aef752013-09-12 17:45:53 -070091
92
93/* maximum time since last capture buffer update before resetting capture buffer. This means
94 that the framework has stopped playing audio and we must start returning silence */
95#define MAX_STALL_TIME_MS 1000
96
97#define CAPTURE_BUF_SIZE 65536 /* "64k should be enough for everyone" */
98
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -070099#define DISCARD_MEASUREMENTS_TIME_MS 2000 /* discard measurements older than this number of ms */
100
101/* maximum number of buffers for which we keep track of the measurements */
102#define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25 /* note: buffer index is stored in uint8_t */
103
104typedef struct buffer_stats_s {
105 bool is_valid;
106 uint16_t peak_u16; /* the positive peak of the absolute value of the samples in a buffer */
107 float rms_squared; /* the average square of the samples in a buffer */
108} buffer_stats_t;
Eric Laurentc4aef752013-09-12 17:45:53 -0700109
110typedef struct visualizer_context_s {
111 effect_context_t common;
112
113 uint32_t capture_idx;
114 uint32_t capture_size;
115 uint32_t scaling_mode;
116 uint32_t last_capture_idx;
117 uint32_t latency;
118 struct timespec buffer_update_time;
119 uint8_t capture_buf[CAPTURE_BUF_SIZE];
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700120 /* for measurements */
121 uint8_t channel_count; /* to avoid recomputing it every time a buffer is processed */
122 uint32_t meas_mode;
123 uint8_t meas_wndw_size_in_buffers;
124 uint8_t meas_buffer_idx;
125 buffer_stats_t past_meas[MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS];
Eric Laurentc4aef752013-09-12 17:45:53 -0700126} visualizer_context_t;
127
128
129extern const struct effect_interface_s effect_interface;
130
131/* Offload visualizer UUID: 7a8044a0-1a71-11e3-a184-0002a5d5c51b */
132const effect_descriptor_t visualizer_descriptor = {
133 {0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
134 {0x7a8044a0, 0x1a71, 0x11e3, 0xa184, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
135 EFFECT_CONTROL_API_VERSION,
136 (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_HW_ACC_TUNNEL ),
137 0, /* TODO */
138 1,
139 "QCOM MSM offload visualizer",
140 "The Android Open Source Project",
141};
142
143const effect_descriptor_t *descriptors[] = {
144 &visualizer_descriptor,
145 NULL,
146};
147
Aniket Kumar Lata72a3cea2018-04-06 13:56:37 -0700148struct pcm_capture_config {
149 int snd_card_num;
150 int capture_device_id;
151};
152
153struct pcm_capture_config capture_config;
Eric Laurentc4aef752013-09-12 17:45:53 -0700154
155pthread_once_t once = PTHREAD_ONCE_INIT;
156int init_status;
157
158/* list of created effects. Updated by visualizer_hal_start_output()
159 * and visualizer_hal_stop_output() */
160struct listnode created_effects_list;
161/* list of active output streams. Updated by visualizer_hal_start_output()
162 * and visualizer_hal_stop_output() */
163struct 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 */
167pthread_t capture_thread;
168/* lock must be held when modifying or accessing created_effects_list or active_outputs_list */
169pthread_mutex_t lock;
170/* thread_lock must be held when starting or stopping the capture thread.
171 * Locking order: thread_lock -> lock */
172pthread_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. */
175pthread_cond_t cond;
176/* true when requesting the capture thread to exit */
177bool exit_thread;
178/* 0 if the capture thread was created successfully */
179int thread_status;
180
Eric Laurentc4aef752013-09-12 17:45:53 -0700181#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
Eric Laurentc4aef752013-09-12 17:45:53 -0700187/* 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
193struct 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
209static 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
222int lib_init() {
223 pthread_once(&once, init_once);
224 return init_status;
225}
226
227bool 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
241output_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
255void 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);
Haynes Mathew George41f86652014-06-17 14:22:15 -0700266 if (context->ops.start)
267 context->ops.start(context, output);
Eric Laurentc4aef752013-09-12 17:45:53 -0700268}
269
270void 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) {
Haynes Mathew George41f86652014-06-17 14:22:15 -0700278 if (context->ops.stop)
279 context->ops.stop(context, output);
Eric Laurentc4aef752013-09-12 17:45:53 -0700280 list_remove(&context->output_node);
281 return;
282 }
283 }
284}
285
286bool 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);
Haynes Mathew George41f86652014-06-17 14:22:15 -0700299 if (fx_ctxt->state == EFFECT_STATE_ACTIVE && fx_ctxt->ops.process != NULL)
Eric Laurentc4aef752013-09-12 17:45:53 -0700300 return true;
301 }
302 }
303 return false;
304}
305
306int 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
Ravi Kumar Alamanda8652b462014-11-14 16:51:10 -0800310 if (value && acdb_send_audio_cal)
311 acdb_send_audio_cal(AFE_PROXY_ACDB_ID, ACDB_DEV_TYPE_OUT);
312
Eric Laurentc4aef752013-09-12 17:45:53 -0700313 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
Haynes Mathew Georgecc9649b2014-06-10 15:08:39 -0700325void *capture_thread_loop(void *arg __unused)
Eric Laurentc4aef752013-09-12 17:45:53 -0700326{
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
Aniket Kumar Lata72a3cea2018-04-06 13:56:37 -0700343 mixer = mixer_open(capture_config.snd_card_num);
Eric Laurentc4aef752013-09-12 17:45:53 -0700344 while (mixer == NULL && retry_num < RETRY_NUMBER) {
345 usleep(RETRY_US);
Aniket Kumar Lata72a3cea2018-04-06 13:56:37 -0700346 mixer = mixer_open(capture_config.snd_card_num);
Eric Laurentc4aef752013-09-12 17:45:53 -0700347 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) {
Aniket Kumar Lata72a3cea2018-04-06 13:56:37 -0700362 pcm = pcm_open(capture_config.snd_card_num,
363 capture_config.capture_device_id,
Eric Laurentc4aef752013-09-12 17:45:53 -0700364 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);
Haynes Mathew George41f86652014-06-17 14:22:15 -0700406 if (fx_ctxt->ops.process != NULL)
407 fx_ctxt->ops.process(fx_ctxt, &buf, &buf);
Eric Laurentc4aef752013-09-12 17:45:53 -0700408 }
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")))
Aniket Kumar Lata72a3cea2018-04-06 13:56:37 -0700433int visualizer_hal_start_output(audio_io_handle_t output, int pcm_id,
434 int card_number, int pcm_capture_id) {
David Benjaminc9d8d892016-09-21 12:04:55 -0400435 int ret = 0;
Eric Laurentc4aef752013-09-12 17:45:53 -0700436 struct listnode *node;
437
Haynes Mathew George41f86652014-06-17 14:22:15 -0700438 ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
Eric Laurentc4aef752013-09-12 17:45:53 -0700439
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
Aniket Kumar Lata72a3cea2018-04-06 13:56:37 -0700451 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
Eric Laurentc4aef752013-09-12 17:45:53 -0700456 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) {
Haynes Mathew George41f86652014-06-17 14:22:15 -0700465 if (fx_ctxt->ops.start)
466 fx_ctxt->ops.start(fx_ctxt, out_ctxt);
Eric Laurentc4aef752013-09-12 17:45:53 -0700467 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
478exit:
479 pthread_mutex_unlock(&lock);
480 pthread_mutex_unlock(&thread_lock);
481 return ret;
482}
483
484__attribute__ ((visibility ("default")))
Haynes Mathew George41f86652014-06-17 14:22:15 -0700485int visualizer_hal_stop_output(audio_io_handle_t output, int pcm_id) {
David Benjaminc9d8d892016-09-21 12:04:55 -0400486 int ret = 0;
Eric Laurentc4aef752013-09-12 17:45:53 -0700487 struct listnode *node;
Haynes Mathew George41f86652014-06-17 14:22:15 -0700488 struct listnode *fx_node;
Eric Laurentc4aef752013-09-12 17:45:53 -0700489 output_context_t *out_ctxt;
490
Haynes Mathew George41f86652014-06-17 14:22:15 -0700491 ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
Eric Laurentc4aef752013-09-12 17:45:53 -0700492
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 }
Haynes Mathew George41f86652014-06-17 14:22:15 -0700505 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 }
Eric Laurentc4aef752013-09-12 17:45:53 -0700512 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
528exit:
529 pthread_mutex_unlock(&lock);
530 pthread_mutex_unlock(&thread_lock);
531 return ret;
532}
533
534
535/*
536 * Effect operations
537 */
538
539int 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
557void get_config(effect_context_t *context, effect_config_t *config)
558{
559 *config = context->config;
560}
561
562
563/*
564 * Visualizer operations
565 */
566
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700567uint32_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
Eric Laurentc4aef752013-09-12 17:45:53 -0700584int 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
596int visualizer_init(effect_context_t *context)
597{
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700598 int32_t i;
599
Eric Laurentc4aef752013-09-12 17:45:53 -0700600 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
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700622 // measurement initialization
Eric Laurent0de8d1f2014-07-01 20:34:45 -0700623 visu_ctxt->channel_count = audio_channel_count_from_out_mask(context->config.inputCfg.channels);
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700624 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
Eric Laurentc4aef752013-09-12 17:45:53 -0700633 set_config(context, &context->config);
634
Ravi Kumar Alamanda8652b462014-11-14 16:51:10 -0800635 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
Eric Laurentc4aef752013-09-12 17:45:53 -0700648 return 0;
649}
650
651int 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;
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700674 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;
Eric Laurentc4aef752013-09-12 17:45:53 -0700680 default:
681 p->status = -EINVAL;
682 }
683 return 0;
684}
685
Haynes Mathew Georgecc9649b2014-06-10 15:08:39 -0700686int visualizer_set_parameter(effect_context_t *context, effect_param_t *p, uint32_t size __unused)
Eric Laurentc4aef752013-09-12 17:45:53 -0700687{
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;
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700707 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;
Eric Laurentc4aef752013-09-12 17:45:53 -0700711 default:
712 return -EINVAL;
713 }
714 return 0;
715}
716
717/* Real process function called from capture thread. Called with lock held */
718int 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
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700734 // 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
Eric Laurentc4aef752013-09-12 17:45:53 -0700758 /* 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
Haynes Mathew Georgecc9649b2014-06-10 15:08:39 -0700819int visualizer_command(effect_context_t * context, uint32_t cmdCode, uint32_t cmdSize __unused,
820 void *pCmdData __unused, uint32_t *replySize, void *pReplyData)
Eric Laurentc4aef752013-09-12 17:45:53 -0700821{
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;
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700837 const uint32_t delta_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt);
838 latency_ms -= delta_ms;
839 if (latency_ms < 0) {
840 latency_ms = 0;
Eric Laurentc4aef752013-09-12 17:45:53 -0700841 }
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700842 const uint32_t delta_smp = context->config.inputCfg.samplingRate * latency_ms / 1000;
Eric Laurentc4aef752013-09-12 17:45:53 -0700843
844 int32_t capture_point = visu_ctxt->capture_idx - visu_ctxt->capture_size - delta_smp;
845 int32_t capture_size = visu_ctxt->capture_size;
846 if (capture_point < 0) {
847 int32_t size = -capture_point;
848 if (size > capture_size)
849 size = capture_size;
850
851 memcpy(pReplyData,
852 visu_ctxt->capture_buf + CAPTURE_BUF_SIZE + capture_point,
853 size);
854 pReplyData = (void *)((size_t)pReplyData + size);
855 capture_size -= size;
856 capture_point = 0;
857 }
858 memcpy(pReplyData,
859 visu_ctxt->capture_buf + capture_point,
860 capture_size);
861
862
863 /* if audio framework has stopped playing audio although the effect is still
864 * active we must clear the capture buffer to return silence */
865 if ((visu_ctxt->last_capture_idx == visu_ctxt->capture_idx) &&
866 (visu_ctxt->buffer_update_time.tv_sec != 0)) {
867 if (delta_ms > MAX_STALL_TIME_MS) {
868 ALOGV("%s capture going to idle", __func__);
869 visu_ctxt->buffer_update_time.tv_sec = 0;
870 memset(pReplyData, 0x80, visu_ctxt->capture_size);
871 }
872 }
873 visu_ctxt->last_capture_idx = visu_ctxt->capture_idx;
874 } else {
875 memset(pReplyData, 0x80, visu_ctxt->capture_size);
876 }
877 break;
878
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700879 case VISUALIZER_CMD_MEASURE: {
rago2fa52192016-08-22 17:59:38 -0700880 if (pReplyData == NULL || replySize == NULL ||
881 *replySize < (sizeof(int32_t) * MEASUREMENT_COUNT)) {
rago314274d2016-10-07 18:13:29 -0700882 if (replySize == NULL) {
883 ALOGV("%s VISUALIZER_CMD_MEASURE error replySize NULL", __func__);
884 } else {
885 ALOGV("%s VISUALIZER_CMD_MEASURE error *replySize %u <"
886 "(sizeof(int32_t) * MEASUREMENT_COUNT) %zu",
887 __func__, *replySize, sizeof(int32_t) * MEASUREMENT_COUNT);
888 }
rago2fa52192016-08-22 17:59:38 -0700889 android_errorWriteLog(0x534e4554, "30229821");
890 return -EINVAL;
891 }
Jean-Michel Trivia6c11c12013-09-24 15:08:56 -0700892 uint16_t peak_u16 = 0;
893 float sum_rms_squared = 0.0f;
894 uint8_t nb_valid_meas = 0;
895 /* reset measurements if last measurement was too long ago (which implies stored
896 * measurements aren't relevant anymore and shouldn't bias the new one) */
897 const int32_t delay_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt);
898 if (delay_ms > DISCARD_MEASUREMENTS_TIME_MS) {
899 uint32_t i;
900 ALOGV("Discarding measurements, last measurement is %dms old", delay_ms);
901 for (i=0 ; i<visu_ctxt->meas_wndw_size_in_buffers ; i++) {
902 visu_ctxt->past_meas[i].is_valid = false;
903 visu_ctxt->past_meas[i].peak_u16 = 0;
904 visu_ctxt->past_meas[i].rms_squared = 0;
905 }
906 visu_ctxt->meas_buffer_idx = 0;
907 } else {
908 /* only use actual measurements, otherwise the first RMS measure happening before
909 * MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially
910 * low */
911 uint32_t i;
912 for (i=0 ; i < visu_ctxt->meas_wndw_size_in_buffers ; i++) {
913 if (visu_ctxt->past_meas[i].is_valid) {
914 if (visu_ctxt->past_meas[i].peak_u16 > peak_u16) {
915 peak_u16 = visu_ctxt->past_meas[i].peak_u16;
916 }
917 sum_rms_squared += visu_ctxt->past_meas[i].rms_squared;
918 nb_valid_meas++;
919 }
920 }
921 }
922 float rms = nb_valid_meas == 0 ? 0.0f : sqrtf(sum_rms_squared / nb_valid_meas);
923 int32_t* p_int_reply_data = (int32_t*)pReplyData;
924 /* convert from I16 sample values to mB and write results */
925 if (rms < 0.000016f) {
926 p_int_reply_data[MEASUREMENT_IDX_RMS] = -9600; //-96dB
927 } else {
928 p_int_reply_data[MEASUREMENT_IDX_RMS] = (int32_t) (2000 * log10(rms / 32767.0f));
929 }
930 if (peak_u16 == 0) {
931 p_int_reply_data[MEASUREMENT_IDX_PEAK] = -9600; //-96dB
932 } else {
933 p_int_reply_data[MEASUREMENT_IDX_PEAK] = (int32_t) (2000 * log10(peak_u16 / 32767.0f));
934 }
935 ALOGV("VISUALIZER_CMD_MEASURE peak=%d (%dmB), rms=%.1f (%dmB)",
936 peak_u16, p_int_reply_data[MEASUREMENT_IDX_PEAK],
937 rms, p_int_reply_data[MEASUREMENT_IDX_RMS]);
938 }
939 break;
940
Eric Laurentc4aef752013-09-12 17:45:53 -0700941 default:
942 ALOGW("%s invalid command %d", __func__, cmdCode);
943 return -EINVAL;
944 }
945 return 0;
946}
947
948
949/*
950 * Effect Library Interface Implementation
951 */
952
953int effect_lib_create(const effect_uuid_t *uuid,
Haynes Mathew Georgecc9649b2014-06-10 15:08:39 -0700954 int32_t sessionId __unused,
Eric Laurentc4aef752013-09-12 17:45:53 -0700955 int32_t ioId,
956 effect_handle_t *pHandle) {
957 int ret;
958 int i;
959
960 if (lib_init() != 0)
961 return init_status;
962
963 if (pHandle == NULL || uuid == NULL)
964 return -EINVAL;
965
966 for (i = 0; descriptors[i] != NULL; i++) {
967 if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0)
968 break;
969 }
970
971 if (descriptors[i] == NULL)
972 return -EINVAL;
973
974 effect_context_t *context;
975 if (memcmp(uuid, &visualizer_descriptor.uuid, sizeof(effect_uuid_t)) == 0) {
976 visualizer_context_t *visu_ctxt = (visualizer_context_t *)calloc(1,
977 sizeof(visualizer_context_t));
978 context = (effect_context_t *)visu_ctxt;
979 context->ops.init = visualizer_init;
980 context->ops.reset = visualizer_reset;
981 context->ops.process = visualizer_process;
982 context->ops.set_parameter = visualizer_set_parameter;
983 context->ops.get_parameter = visualizer_get_parameter;
984 context->ops.command = visualizer_command;
Haynes Mathew George41f86652014-06-17 14:22:15 -0700985 context->desc = &visualizer_descriptor;
Eric Laurentc4aef752013-09-12 17:45:53 -0700986 } else {
987 return -EINVAL;
988 }
989
990 context->itfe = &effect_interface;
991 context->state = EFFECT_STATE_UNINITIALIZED;
992 context->out_handle = (audio_io_handle_t)ioId;
Eric Laurentc4aef752013-09-12 17:45:53 -0700993
994 ret = context->ops.init(context);
995 if (ret < 0) {
996 ALOGW("%s init failed", __func__);
997 free(context);
998 return ret;
999 }
1000
1001 context->state = EFFECT_STATE_INITIALIZED;
1002
1003 pthread_mutex_lock(&lock);
1004 list_add_tail(&created_effects_list, &context->effects_list_node);
1005 output_context_t *out_ctxt = get_output(ioId);
1006 if (out_ctxt != NULL)
1007 add_effect_to_output(out_ctxt, context);
1008 pthread_mutex_unlock(&lock);
1009
1010 *pHandle = (effect_handle_t)context;
1011
1012 ALOGV("%s created context %p", __func__, context);
1013
1014 return 0;
1015
1016}
1017
1018int effect_lib_release(effect_handle_t handle) {
1019 effect_context_t *context = (effect_context_t *)handle;
1020 int status;
1021
1022 if (lib_init() != 0)
1023 return init_status;
1024
1025 ALOGV("%s context %p", __func__, handle);
1026 pthread_mutex_lock(&lock);
1027 status = -EINVAL;
1028 if (effect_exists(context)) {
1029 output_context_t *out_ctxt = get_output(context->out_handle);
1030 if (out_ctxt != NULL)
1031 remove_effect_from_output(out_ctxt, context);
1032 list_remove(&context->effects_list_node);
1033 if (context->ops.release)
1034 context->ops.release(context);
1035 free(context);
1036 status = 0;
1037 }
1038 pthread_mutex_unlock(&lock);
1039
1040 return status;
1041}
1042
1043int effect_lib_get_descriptor(const effect_uuid_t *uuid,
1044 effect_descriptor_t *descriptor) {
1045 int i;
1046
1047 if (lib_init() != 0)
1048 return init_status;
1049
1050 if (descriptor == NULL || uuid == NULL) {
1051 ALOGV("%s called with NULL pointer", __func__);
1052 return -EINVAL;
1053 }
1054
1055 for (i = 0; descriptors[i] != NULL; i++) {
1056 if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
1057 *descriptor = *descriptors[i];
1058 return 0;
1059 }
1060 }
1061
1062 return -EINVAL;
1063}
1064
1065/*
1066 * Effect Control Interface Implementation
1067 */
1068
1069 /* Stub function for effect interface: never called for offloaded effects */
1070int effect_process(effect_handle_t self,
Haynes Mathew Georgecc9649b2014-06-10 15:08:39 -07001071 audio_buffer_t *inBuffer __unused,
1072 audio_buffer_t *outBuffer __unused)
Eric Laurentc4aef752013-09-12 17:45:53 -07001073{
1074 effect_context_t * context = (effect_context_t *)self;
1075 int status = 0;
1076
1077 ALOGW("%s Called ?????", __func__);
1078
1079 pthread_mutex_lock(&lock);
1080 if (!effect_exists(context)) {
1081 status = -EINVAL;
1082 goto exit;
1083 }
1084
1085 if (context->state != EFFECT_STATE_ACTIVE) {
1086 status = -EINVAL;
1087 goto exit;
1088 }
1089
1090exit:
1091 pthread_mutex_unlock(&lock);
1092 return status;
1093}
1094
1095int effect_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
1096 void *pCmdData, uint32_t *replySize, void *pReplyData)
1097{
1098
1099 effect_context_t * context = (effect_context_t *)self;
1100 int retsize;
1101 int status = 0;
1102
1103 pthread_mutex_lock(&lock);
1104
1105 if (!effect_exists(context)) {
1106 status = -EINVAL;
1107 goto exit;
1108 }
1109
1110 if (context == NULL || context->state == EFFECT_STATE_UNINITIALIZED) {
1111 status = -EINVAL;
1112 goto exit;
1113 }
1114
1115// ALOGV_IF(cmdCode != VISUALIZER_CMD_CAPTURE,
1116// "%s command %d cmdSize %d", __func__, cmdCode, cmdSize);
1117
1118 switch (cmdCode) {
1119 case EFFECT_CMD_INIT:
1120 if (pReplyData == NULL || *replySize != sizeof(int)) {
1121 status = -EINVAL;
1122 goto exit;
1123 }
1124 if (context->ops.init)
1125 *(int *) pReplyData = context->ops.init(context);
1126 else
1127 *(int *) pReplyData = 0;
1128 break;
1129 case EFFECT_CMD_SET_CONFIG:
1130 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
1131 || pReplyData == NULL || *replySize != sizeof(int)) {
1132 status = -EINVAL;
1133 goto exit;
1134 }
1135 *(int *) pReplyData = set_config(context, (effect_config_t *) pCmdData);
1136 break;
1137 case EFFECT_CMD_GET_CONFIG:
1138 if (pReplyData == NULL ||
1139 *replySize != sizeof(effect_config_t)) {
1140 status = -EINVAL;
1141 goto exit;
1142 }
1143 if (!context->offload_enabled) {
1144 status = -EINVAL;
1145 goto exit;
1146 }
1147
1148 get_config(context, (effect_config_t *)pReplyData);
1149 break;
1150 case EFFECT_CMD_RESET:
1151 if (context->ops.reset)
1152 context->ops.reset(context);
1153 break;
1154 case EFFECT_CMD_ENABLE:
1155 if (pReplyData == NULL || *replySize != sizeof(int)) {
1156 status = -EINVAL;
1157 goto exit;
1158 }
1159 if (context->state != EFFECT_STATE_INITIALIZED) {
1160 status = -ENOSYS;
1161 goto exit;
1162 }
1163 context->state = EFFECT_STATE_ACTIVE;
1164 if (context->ops.enable)
1165 context->ops.enable(context);
1166 pthread_cond_signal(&cond);
1167 ALOGV("%s EFFECT_CMD_ENABLE", __func__);
1168 *(int *)pReplyData = 0;
1169 break;
1170 case EFFECT_CMD_DISABLE:
1171 if (pReplyData == NULL || *replySize != sizeof(int)) {
1172 status = -EINVAL;
1173 goto exit;
1174 }
1175 if (context->state != EFFECT_STATE_ACTIVE) {
1176 status = -ENOSYS;
1177 goto exit;
1178 }
1179 context->state = EFFECT_STATE_INITIALIZED;
1180 if (context->ops.disable)
1181 context->ops.disable(context);
1182 pthread_cond_signal(&cond);
1183 ALOGV("%s EFFECT_CMD_DISABLE", __func__);
1184 *(int *)pReplyData = 0;
1185 break;
1186 case EFFECT_CMD_GET_PARAM: {
1187 if (pCmdData == NULL ||
1188 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
1189 pReplyData == NULL ||
1190 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
1191 status = -EINVAL;
1192 goto exit;
1193 }
1194 if (!context->offload_enabled) {
1195 status = -EINVAL;
1196 goto exit;
1197 }
1198 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
1199 effect_param_t *p = (effect_param_t *)pReplyData;
1200 if (context->ops.get_parameter)
1201 context->ops.get_parameter(context, p, replySize);
1202 } break;
1203 case EFFECT_CMD_SET_PARAM: {
1204 if (pCmdData == NULL ||
1205 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
1206 pReplyData == NULL || *replySize != sizeof(int32_t)) {
1207 status = -EINVAL;
1208 goto exit;
1209 }
1210 *(int32_t *)pReplyData = 0;
1211 effect_param_t *p = (effect_param_t *)pCmdData;
1212 if (context->ops.set_parameter)
1213 *(int32_t *)pReplyData = context->ops.set_parameter(context, p, *replySize);
1214
1215 } break;
1216 case EFFECT_CMD_SET_DEVICE:
1217 case EFFECT_CMD_SET_VOLUME:
1218 case EFFECT_CMD_SET_AUDIO_MODE:
1219 break;
1220
1221 case EFFECT_CMD_OFFLOAD: {
1222 output_context_t *out_ctxt;
1223
1224 if (cmdSize != sizeof(effect_offload_param_t) || pCmdData == NULL
1225 || pReplyData == NULL || *replySize != sizeof(int)) {
1226 ALOGV("%s EFFECT_CMD_OFFLOAD bad format", __func__);
1227 status = -EINVAL;
1228 break;
1229 }
1230
1231 effect_offload_param_t* offload_param = (effect_offload_param_t*)pCmdData;
1232
1233 ALOGV("%s EFFECT_CMD_OFFLOAD offload %d output %d",
1234 __func__, offload_param->isOffload, offload_param->ioHandle);
1235
1236 *(int *)pReplyData = 0;
1237
1238 context->offload_enabled = offload_param->isOffload;
1239 if (context->out_handle == offload_param->ioHandle)
1240 break;
1241
1242 out_ctxt = get_output(context->out_handle);
1243 if (out_ctxt != NULL)
1244 remove_effect_from_output(out_ctxt, context);
Haynes Mathew George41f86652014-06-17 14:22:15 -07001245
1246 context->out_handle = offload_param->ioHandle;
Eric Laurentc4aef752013-09-12 17:45:53 -07001247 out_ctxt = get_output(offload_param->ioHandle);
1248 if (out_ctxt != NULL)
1249 add_effect_to_output(out_ctxt, context);
1250
Eric Laurentc4aef752013-09-12 17:45:53 -07001251 } break;
1252
1253
1254 default:
1255 if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY && context->ops.command)
1256 status = context->ops.command(context, cmdCode, cmdSize,
1257 pCmdData, replySize, pReplyData);
1258 else {
1259 ALOGW("%s invalid command %d", __func__, cmdCode);
1260 status = -EINVAL;
1261 }
1262 break;
1263 }
1264
1265exit:
1266 pthread_mutex_unlock(&lock);
1267
1268// ALOGV_IF(cmdCode != VISUALIZER_CMD_CAPTURE,"%s DONE", __func__);
1269 return status;
1270}
1271
1272/* Effect Control Interface Implementation: get_descriptor */
1273int effect_get_descriptor(effect_handle_t self,
1274 effect_descriptor_t *descriptor)
1275{
1276 effect_context_t *context = (effect_context_t *)self;
1277
1278 if (!effect_exists(context))
1279 return -EINVAL;
1280
1281 if (descriptor == NULL)
1282 return -EINVAL;
1283
1284 *descriptor = *context->desc;
1285
1286 return 0;
1287}
1288
1289/* effect_handle_t interface implementation for visualizer effect */
1290const struct effect_interface_s effect_interface = {
1291 effect_process,
1292 effect_command,
1293 effect_get_descriptor,
1294 NULL,
1295};
1296
1297__attribute__ ((visibility ("default")))
1298audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
synergy dev19af91c2013-12-17 17:36:30 -08001299 .tag = AUDIO_EFFECT_LIBRARY_TAG,
1300 .version = EFFECT_LIBRARY_API_VERSION,
1301 .name = "Visualizer Library",
1302 .implementor = "The Android Open Source Project",
1303 .create_effect = effect_lib_create,
1304 .release_effect = effect_lib_release,
1305 .get_descriptor = effect_lib_get_descriptor,
Eric Laurentc4aef752013-09-12 17:45:53 -07001306};