1 /* Copyright 2018 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6 #include <inttypes.h>
7 #include <string.h>
8 #include <syslog.h>
9
10 #include <webrtc-apm/webrtc_apm.h>
11
12 #include "byte_buffer.h"
13 #include "cras_apm_list.h"
14 #include "cras_audio_area.h"
15 #include "cras_audio_format.h"
16 #include "cras_dsp_pipeline.h"
17 #include "cras_iodev.h"
18 #include "cras_iodev_list.h"
19 #include "dsp_util.h"
20 #include "dumper.h"
21 #include "float_buffer.h"
22 #include "iniparser_wrapper.h"
23 #include "utlist.h"
24
25 #define AEC_CONFIG_NAME "aec.ini"
26 #define APM_CONFIG_NAME "apm.ini"
27
28 /*
29 * Structure holding a WebRTC audio processing module and necessary
30 * info to process and transfer input buffer from device to stream.
31 *
32 * Below chart describes the buffer structure inside APM and how an input buffer
33 * flows from a device through the APM to stream. APM processes audio buffers in
34 * fixed 10ms width, and that's the main reason we need two copies of the
35 * buffer:
36 * (1) to cache input buffer from device until 10ms size is filled.
37 * (2) to store the interleaved buffer, of 10ms size also, after APM processing.
38 *
39 * ________ _______ _______________________________
40 * | | | | |_____________APM ____________|
41 * |input |-> | DSP |---> || | | || -> stream 1
42 * |device| | | | || float buf | -> | byte buf ||
43 * |______| |_____| | ||___________| |__________||
44 * | |_____________________________|
45 * | _______________________________
46 * |-> | APM 2 | -> stream 2
47 * | |_____________________________|
48 * | ...
49 * |
50 * |------------------------------------> stream N
51 *
52 * Members:
53 * apm_ptr - An APM instance from libwebrtc_audio_processing
54 * dev_ptr - Pointer to the device this APM is associated with.
55 * buffer - Stores the processed/interleaved data ready for stream to read.
56 * fbuffer - Stores the floating pointer buffer from input device waiting
57 * for APM to process.
58 * dev_fmt - The format used by the iodev this APM attaches to.
59 * fmt - The audio data format configured for this APM.
60 * area - The cras_audio_area used for copying processed data to client
61 * stream.
62 * work_queue - A task queue instance created and destroyed by
63 * libwebrtc_apm.
64 * is_aec_use_case - True if the input and output devices pair is in the
65 * typical AEC use case. This flag decides whether to use settings
66 * tuned specifically for this hardware if exists. Otherwise it uses
67 * the generic settings like run inside browser.
68 */
69 struct cras_apm {
70 webrtc_apm apm_ptr;
71 void *dev_ptr;
72 struct byte_buffer *buffer;
73 struct float_buffer *fbuffer;
74 struct cras_audio_format dev_fmt;
75 struct cras_audio_format fmt;
76 struct cras_audio_area *area;
77 void *work_queue;
78 bool is_aec_use_case;
79 struct cras_apm *prev, *next;
80 };
81
82 /*
83 * Lists of cras_apm instances created for a stream. A stream may
84 * have more than one cras_apm when multiple input devices are
85 * enabled. The most common scenario is the silent input iodev be
86 * enabled when CRAS switches active input device.
87 *
88 * Note that cras_apm_list is owned and modified in main thread.
89 * Only in synchronized audio thread event this cras_apm_list is safe
90 * to access for passing single APM instance between threads.
91 */
92 struct cras_apm_list {
93 void *stream_ptr;
94 uint64_t effects;
95 struct cras_apm *apms;
96 struct cras_apm_list *prev, *next;
97 };
98
99 /*
100 * Wrappers of APM instances that are active, which means it is associated
101 * to a dev/stream pair in audio thread and ready for processing.
102 *
103 * Members:
104 * apm - The APM for audio data processing.
105 * stream_ptr - Stream pointer from the associated dev/stream pair.
106 * effects - The effecets bit map of APM.
107 */
108 struct active_apm {
109 struct cras_apm *apm;
110 void *stream_ptr;
111 int effects;
112 struct active_apm *prev, *next;
113 } * active_apms;
114
115 /*
116 * Object used to analyze playback audio from output iodev. It is responsible
117 * to get buffer containing latest output data and provide it to the APM
118 * instances which want to analyze reverse stream.
119 * Member:
120 * ext - The interface implemented to process reverse(output) stream
121 * data in various formats.
122 * fbuf - Middle buffer holding reverse data for APMs to analyze.
123 * odev - Pointer to the output iodev playing audio as the reverse
124 * stream. NULL if there's no playback stream.
125 * dev_rate - The sample rate odev is opened for.
126 * process_reverse - Flag to indicate if there's APM has effect that
127 * needs to process reverse stream.
128 */
129 struct cras_apm_reverse_module {
130 struct ext_dsp_module ext;
131 struct float_buffer *fbuf;
132 struct cras_iodev *odev;
133 unsigned int dev_rate;
134 unsigned process_reverse;
135 };
136
137 static struct cras_apm_reverse_module *rmodule = NULL;
138 static const char *aec_config_dir = NULL;
139 static char ini_name[MAX_INI_NAME_LENGTH + 1];
140 static dictionary *aec_ini = NULL;
141 static dictionary *apm_ini = NULL;
142
143 /* Update the global process reverse flag. Should be called when apms are added
144 * or removed. */
update_process_reverse_flag()145 static void update_process_reverse_flag()
146 {
147 struct active_apm *active;
148
149 if (!rmodule)
150 return;
151 rmodule->process_reverse = 0;
152 DL_FOREACH (active_apms, active) {
153 rmodule->process_reverse |=
154 !!(active->effects & APM_ECHO_CANCELLATION);
155 }
156 }
157
apm_destroy(struct cras_apm ** apm)158 static void apm_destroy(struct cras_apm **apm)
159 {
160 if (*apm == NULL)
161 return;
162 byte_buffer_destroy(&(*apm)->buffer);
163 float_buffer_destroy(&(*apm)->fbuffer);
164 cras_audio_area_destroy((*apm)->area);
165
166 /* Any unfinished AEC dump handle will be closed. */
167 webrtc_apm_destroy((*apm)->apm_ptr);
168 free(*apm);
169 *apm = NULL;
170 }
171
cras_apm_list_create(void * stream_ptr,uint64_t effects)172 struct cras_apm_list *cras_apm_list_create(void *stream_ptr, uint64_t effects)
173 {
174 struct cras_apm_list *list;
175
176 if (effects == 0)
177 return NULL;
178
179 list = (struct cras_apm_list *)calloc(1, sizeof(*list));
180 if (list == NULL) {
181 syslog(LOG_ERR, "No memory in creating apm list");
182 return NULL;
183 }
184 list->stream_ptr = stream_ptr;
185 list->effects = effects;
186 list->apms = NULL;
187
188 return list;
189 }
190
get_active_apm(void * stream_ptr,void * dev_ptr)191 static struct active_apm *get_active_apm(void *stream_ptr, void *dev_ptr)
192 {
193 struct active_apm *active;
194
195 DL_FOREACH (active_apms, active) {
196 if ((active->apm->dev_ptr == dev_ptr) &&
197 (active->stream_ptr == stream_ptr))
198 return active;
199 }
200 return NULL;
201 }
202
cras_apm_list_get_active_apm(void * stream_ptr,void * dev_ptr)203 struct cras_apm *cras_apm_list_get_active_apm(void *stream_ptr, void *dev_ptr)
204 {
205 struct active_apm *active = get_active_apm(stream_ptr, dev_ptr);
206 return active ? active->apm : NULL;
207 }
208
cras_apm_list_get_effects(struct cras_apm_list * list)209 uint64_t cras_apm_list_get_effects(struct cras_apm_list *list)
210 {
211 if (list == NULL)
212 return 0;
213 else
214 return list->effects;
215 }
216
cras_apm_list_remove_apm(struct cras_apm_list * list,void * dev_ptr)217 void cras_apm_list_remove_apm(struct cras_apm_list *list, void *dev_ptr)
218 {
219 struct cras_apm *apm;
220
221 DL_FOREACH (list->apms, apm) {
222 if (apm->dev_ptr == dev_ptr) {
223 DL_DELETE(list->apms, apm);
224 apm_destroy(&apm);
225 }
226 }
227 }
228
229 /*
230 * WebRTC APM handles no more than stereo + keyboard mic channels.
231 * Ignore keyboard mic feature for now because that requires processing on
232 * mixed buffer from two input devices. Based on that we should modify the best
233 * channel layout for APM use.
234 * Args:
235 * apm_fmt - Pointer to a format struct already filled with the value of
236 * the open device format. Its content may be modified for APM use.
237 */
get_best_channels(struct cras_audio_format * apm_fmt)238 static void get_best_channels(struct cras_audio_format *apm_fmt)
239 {
240 int ch;
241 int8_t layout[CRAS_CH_MAX];
242
243 /* Using the format from dev_fmt is dangerous because input device
244 * could have wild configurations like unuse the 1st channel and
245 * connects 2nd channel to the only mic. Data in the first channel
246 * is what APM cares about so always construct a new channel layout
247 * containing subset of original channels that matches either FL, FR,
248 * or FC.
249 * TODO(hychao): extend the logic when we have a stream that wants
250 * to record channels like RR(rear right).
251 */
252 for (ch = 0; ch < CRAS_CH_MAX; ch++)
253 layout[ch] = -1;
254
255 apm_fmt->num_channels = 0;
256 if (apm_fmt->channel_layout[CRAS_CH_FL] != -1)
257 layout[CRAS_CH_FL] = apm_fmt->num_channels++;
258 if (apm_fmt->channel_layout[CRAS_CH_FR] != -1)
259 layout[CRAS_CH_FR] = apm_fmt->num_channels++;
260 if (apm_fmt->channel_layout[CRAS_CH_FC] != -1)
261 layout[CRAS_CH_FC] = apm_fmt->num_channels++;
262
263 for (ch = 0; ch < CRAS_CH_MAX; ch++)
264 apm_fmt->channel_layout[ch] = layout[ch];
265 }
266
cras_apm_list_add_apm(struct cras_apm_list * list,void * dev_ptr,const struct cras_audio_format * dev_fmt,bool is_aec_use_case)267 struct cras_apm *cras_apm_list_add_apm(struct cras_apm_list *list,
268 void *dev_ptr,
269 const struct cras_audio_format *dev_fmt,
270 bool is_aec_use_case)
271 {
272 struct cras_apm *apm;
273
274 DL_FOREACH (list->apms, apm)
275 if (apm->dev_ptr == dev_ptr)
276 return apm;
277
278 // TODO(hychao): Remove the check when we enable more effects.
279 if (!(list->effects & APM_ECHO_CANCELLATION))
280 return NULL;
281
282 apm = (struct cras_apm *)calloc(1, sizeof(*apm));
283
284 /* Configures APM to the format used by input device. If the channel
285 * count is larger than stereo, use the standard channel count/layout
286 * in APM. */
287 apm->dev_fmt = *dev_fmt;
288 apm->fmt = *dev_fmt;
289 get_best_channels(&apm->fmt);
290
291 /* Use tuned settings only when the forward dev(capture) and reverse
292 * dev(playback) both are in typical AEC use case. */
293 apm->is_aec_use_case = is_aec_use_case;
294 if (rmodule->odev) {
295 apm->is_aec_use_case &=
296 cras_iodev_is_aec_use_case(rmodule->odev->active_node);
297 }
298
299 /* Use the configs tuned specifically for internal device. Otherwise
300 * just pass NULL so every other settings will be default. */
301 apm->apm_ptr =
302 apm->is_aec_use_case ?
303 webrtc_apm_create(apm->fmt.num_channels,
304 apm->fmt.frame_rate, aec_ini,
305 apm_ini) :
306 webrtc_apm_create(apm->fmt.num_channels,
307 apm->fmt.frame_rate, NULL, NULL);
308 if (apm->apm_ptr == NULL) {
309 syslog(LOG_ERR,
310 "Fail to create webrtc apm for ch %zu"
311 " rate %zu effect %" PRIu64,
312 dev_fmt->num_channels, dev_fmt->frame_rate,
313 list->effects);
314 free(apm);
315 return NULL;
316 }
317
318 apm->dev_ptr = dev_ptr;
319 apm->work_queue = NULL;
320
321 /* WebRTC APM wants 10 ms equivalence of data to process. */
322 apm->buffer = byte_buffer_create(10 * apm->fmt.frame_rate / 1000 *
323 cras_get_format_bytes(&apm->fmt));
324 apm->fbuffer = float_buffer_create(10 * apm->fmt.frame_rate / 1000,
325 apm->fmt.num_channels);
326 apm->area = cras_audio_area_create(apm->fmt.num_channels);
327 cras_audio_area_config_channels(apm->area, &apm->fmt);
328
329 DL_APPEND(list->apms, apm);
330
331 return apm;
332 }
333
cras_apm_list_start_apm(struct cras_apm_list * list,void * dev_ptr)334 void cras_apm_list_start_apm(struct cras_apm_list *list, void *dev_ptr)
335 {
336 struct active_apm *active;
337 struct cras_apm *apm;
338
339 if (list == NULL)
340 return;
341
342 /* Check if this apm has already been started. */
343 apm = cras_apm_list_get_active_apm(list->stream_ptr, dev_ptr);
344 if (apm)
345 return;
346
347 DL_SEARCH_SCALAR(list->apms, apm, dev_ptr, dev_ptr);
348 if (apm == NULL)
349 return;
350
351 active = (struct active_apm *)calloc(1, sizeof(*active));
352 if (active == NULL) {
353 syslog(LOG_ERR, "No memory to start apm.");
354 return;
355 }
356 active->apm = apm;
357 active->stream_ptr = list->stream_ptr;
358 active->effects = list->effects;
359 DL_APPEND(active_apms, active);
360
361 update_process_reverse_flag();
362 }
363
cras_apm_list_stop_apm(struct cras_apm_list * list,void * dev_ptr)364 void cras_apm_list_stop_apm(struct cras_apm_list *list, void *dev_ptr)
365 {
366 struct active_apm *active;
367
368 if (list == NULL)
369 return;
370
371 active = get_active_apm(list->stream_ptr, dev_ptr);
372 if (active) {
373 DL_DELETE(active_apms, active);
374 free(active);
375 }
376
377 update_process_reverse_flag();
378 }
379
cras_apm_list_destroy(struct cras_apm_list * list)380 int cras_apm_list_destroy(struct cras_apm_list *list)
381 {
382 struct cras_apm *apm;
383
384 DL_FOREACH (list->apms, apm) {
385 DL_DELETE(list->apms, apm);
386 apm_destroy(&apm);
387 }
388 free(list);
389
390 return 0;
391 }
392
393 /*
394 * Determines the iodev to be used as the echo reference for APM reverse
395 * analysis. If there exists the special purpose "echo reference dev" then
396 * use it. Otherwise just use this output iodev.
397 */
get_echo_reference_target(struct cras_iodev * iodev)398 static struct cras_iodev *get_echo_reference_target(struct cras_iodev *iodev)
399 {
400 return iodev->echo_reference_dev ? iodev->echo_reference_dev : iodev;
401 }
402
403 /*
404 * Updates the first enabled output iodev in the list, determine the echo
405 * reference target base on this output iodev, and register rmodule as ext dsp
406 * module to this echo reference target.
407 * When this echo reference iodev is opened and audio data flows through its
408 * dsp pipeline, APMs will anaylize the reverse stream. This is expected to be
409 * called in main thread when output devices enable/dsiable state changes.
410 */
update_first_output_dev_to_process()411 static void update_first_output_dev_to_process()
412 {
413 struct cras_iodev *echo_ref;
414 struct cras_iodev *iodev =
415 cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT);
416
417 if (iodev == NULL)
418 return;
419
420 echo_ref = get_echo_reference_target(iodev);
421
422 /* If rmodule is already tracking echo_ref, do nothing. */
423 if (rmodule->odev == echo_ref)
424 return;
425
426 /* Detach from the old iodev that rmodule was tracking. */
427 if (rmodule->odev) {
428 cras_iodev_set_ext_dsp_module(rmodule->odev, NULL);
429 rmodule->odev = NULL;
430 }
431
432 rmodule->odev = echo_ref;
433 cras_iodev_set_ext_dsp_module(echo_ref, &rmodule->ext);
434 }
435
handle_device_enabled(struct cras_iodev * iodev,void * cb_data)436 static void handle_device_enabled(struct cras_iodev *iodev, void *cb_data)
437 {
438 if (iodev->direction != CRAS_STREAM_OUTPUT)
439 return;
440
441 /* Register to the first enabled output device. */
442 update_first_output_dev_to_process();
443 }
444
handle_device_disabled(struct cras_iodev * iodev,void * cb_data)445 static void handle_device_disabled(struct cras_iodev *iodev, void *cb_data)
446 {
447 struct cras_iodev *echo_ref;
448
449 if (iodev->direction != CRAS_STREAM_OUTPUT)
450 return;
451
452 echo_ref = get_echo_reference_target(iodev);
453
454 if (rmodule->odev == echo_ref) {
455 cras_iodev_set_ext_dsp_module(echo_ref, NULL);
456 rmodule->odev = NULL;
457 }
458
459 /* Register to the first enabled output device. */
460 update_first_output_dev_to_process();
461 }
462
process_reverse(struct float_buffer * fbuf,unsigned int frame_rate)463 static int process_reverse(struct float_buffer *fbuf, unsigned int frame_rate)
464 {
465 struct active_apm *active;
466 int ret;
467 float *const *wp;
468
469 if (float_buffer_writable(fbuf))
470 return 0;
471
472 wp = float_buffer_write_pointer(fbuf);
473
474 DL_FOREACH (active_apms, active) {
475 if (!(active->effects & APM_ECHO_CANCELLATION))
476 continue;
477
478 ret = webrtc_apm_process_reverse_stream_f(active->apm->apm_ptr,
479 fbuf->num_channels,
480 frame_rate, wp);
481 if (ret) {
482 syslog(LOG_ERR, "APM process reverse err");
483 return ret;
484 }
485 }
486 float_buffer_reset(fbuf);
487 return 0;
488 }
489
reverse_data_run(struct ext_dsp_module * ext,unsigned int nframes)490 void reverse_data_run(struct ext_dsp_module *ext, unsigned int nframes)
491 {
492 struct cras_apm_reverse_module *rmod =
493 (struct cras_apm_reverse_module *)ext;
494 unsigned int writable;
495 int i, offset = 0;
496 float *const *wp;
497
498 if (!rmod->process_reverse)
499 return;
500
501 while (nframes) {
502 process_reverse(rmod->fbuf, rmod->dev_rate);
503 writable = float_buffer_writable(rmod->fbuf);
504 writable = MIN(nframes, writable);
505 wp = float_buffer_write_pointer(rmod->fbuf);
506 for (i = 0; i < rmod->fbuf->num_channels; i++)
507 memcpy(wp[i], ext->ports[i] + offset,
508 writable * sizeof(float));
509
510 offset += writable;
511 float_buffer_written(rmod->fbuf, writable);
512 nframes -= writable;
513 }
514 }
515
reverse_data_configure(struct ext_dsp_module * ext,unsigned int buffer_size,unsigned int num_channels,unsigned int rate)516 void reverse_data_configure(struct ext_dsp_module *ext,
517 unsigned int buffer_size, unsigned int num_channels,
518 unsigned int rate)
519 {
520 struct cras_apm_reverse_module *rmod =
521 (struct cras_apm_reverse_module *)ext;
522 if (rmod->fbuf)
523 float_buffer_destroy(&rmod->fbuf);
524 rmod->fbuf = float_buffer_create(rate / 100, num_channels);
525 rmod->dev_rate = rate;
526 }
527
get_aec_ini(const char * config_dir)528 static void get_aec_ini(const char *config_dir)
529 {
530 snprintf(ini_name, MAX_INI_NAME_LENGTH, "%s/%s", config_dir,
531 AEC_CONFIG_NAME);
532 ini_name[MAX_INI_NAME_LENGTH] = '\0';
533
534 if (aec_ini) {
535 iniparser_freedict(aec_ini);
536 aec_ini = NULL;
537 }
538 aec_ini = iniparser_load_wrapper(ini_name);
539 if (aec_ini == NULL)
540 syslog(LOG_INFO, "No aec ini file %s", ini_name);
541 }
542
get_apm_ini(const char * config_dir)543 static void get_apm_ini(const char *config_dir)
544 {
545 snprintf(ini_name, MAX_INI_NAME_LENGTH, "%s/%s", config_dir,
546 APM_CONFIG_NAME);
547 ini_name[MAX_INI_NAME_LENGTH] = '\0';
548
549 if (apm_ini) {
550 iniparser_freedict(apm_ini);
551 apm_ini = NULL;
552 }
553 apm_ini = iniparser_load_wrapper(ini_name);
554 if (apm_ini == NULL)
555 syslog(LOG_INFO, "No apm ini file %s", ini_name);
556 }
557
cras_apm_list_init(const char * device_config_dir)558 int cras_apm_list_init(const char *device_config_dir)
559 {
560 if (rmodule == NULL) {
561 rmodule = (struct cras_apm_reverse_module *)calloc(
562 1, sizeof(*rmodule));
563 rmodule->ext.run = reverse_data_run;
564 rmodule->ext.configure = reverse_data_configure;
565 }
566
567 aec_config_dir = device_config_dir;
568 get_aec_ini(aec_config_dir);
569 get_apm_ini(aec_config_dir);
570
571 update_first_output_dev_to_process();
572 cras_iodev_list_set_device_enabled_callback(
573 handle_device_enabled, handle_device_disabled, rmodule);
574
575 return 0;
576 }
577
cras_apm_list_reload_aec_config()578 void cras_apm_list_reload_aec_config()
579 {
580 if (NULL == aec_config_dir)
581 return;
582
583 get_aec_ini(aec_config_dir);
584 get_apm_ini(aec_config_dir);
585
586 /* Dump the config content at reload only, for debug. */
587 webrtc_apm_dump_configs(apm_ini, aec_ini);
588 }
589
cras_apm_list_deinit()590 int cras_apm_list_deinit()
591 {
592 if (rmodule) {
593 if (rmodule->fbuf)
594 float_buffer_destroy(&rmodule->fbuf);
595 free(rmodule);
596 rmodule = NULL;
597 }
598 return 0;
599 }
600
cras_apm_list_process(struct cras_apm * apm,struct float_buffer * input,unsigned int offset)601 int cras_apm_list_process(struct cras_apm *apm, struct float_buffer *input,
602 unsigned int offset)
603 {
604 unsigned int writable, nframes, nread;
605 int ch, i, j, ret;
606 float *const *wp;
607 float *const *rp;
608
609 nread = float_buffer_level(input);
610 if (nread < offset) {
611 syslog(LOG_ERR, "Process offset exceeds read level");
612 return -EINVAL;
613 }
614
615 writable = float_buffer_writable(apm->fbuffer);
616 writable = MIN(nread - offset, writable);
617
618 nframes = writable;
619 while (nframes) {
620 nread = nframes;
621 wp = float_buffer_write_pointer(apm->fbuffer);
622 rp = float_buffer_read_pointer(input, offset, &nread);
623
624 for (i = 0; i < apm->fbuffer->num_channels; i++) {
625 /* Look up the channel position and copy from
626 * the correct index of |input| buffer.
627 */
628 for (ch = 0; ch < CRAS_CH_MAX; ch++)
629 if (apm->fmt.channel_layout[ch] == i)
630 break;
631 if (ch == CRAS_CH_MAX)
632 continue;
633
634 j = apm->dev_fmt.channel_layout[ch];
635 if (j == -1)
636 continue;
637
638 memcpy(wp[i], rp[j], nread * sizeof(float));
639 }
640
641 nframes -= nread;
642 offset += nread;
643
644 float_buffer_written(apm->fbuffer, nread);
645 }
646
647 /* process and move to int buffer */
648 if ((float_buffer_writable(apm->fbuffer) == 0) &&
649 (buf_queued(apm->buffer) == 0)) {
650 nread = float_buffer_level(apm->fbuffer);
651 rp = float_buffer_read_pointer(apm->fbuffer, 0, &nread);
652 ret = webrtc_apm_process_stream_f(apm->apm_ptr,
653 apm->fmt.num_channels,
654 apm->fmt.frame_rate, rp);
655 if (ret) {
656 syslog(LOG_ERR, "APM process stream f err");
657 return ret;
658 }
659
660 dsp_util_interleave(rp, buf_write_pointer(apm->buffer),
661 apm->fbuffer->num_channels, apm->fmt.format,
662 nread);
663 buf_increment_write(apm->buffer,
664 nread * cras_get_format_bytes(&apm->fmt));
665 float_buffer_reset(apm->fbuffer);
666 }
667
668 return writable;
669 }
670
cras_apm_list_get_processed(struct cras_apm * apm)671 struct cras_audio_area *cras_apm_list_get_processed(struct cras_apm *apm)
672 {
673 uint8_t *buf_ptr;
674
675 buf_ptr = buf_read_pointer_size(apm->buffer, &apm->area->frames);
676 apm->area->frames /= cras_get_format_bytes(&apm->fmt);
677 cras_audio_area_config_buf_pointers(apm->area, &apm->fmt, buf_ptr);
678 return apm->area;
679 }
680
cras_apm_list_put_processed(struct cras_apm * apm,unsigned int frames)681 void cras_apm_list_put_processed(struct cras_apm *apm, unsigned int frames)
682 {
683 buf_increment_read(apm->buffer,
684 frames * cras_get_format_bytes(&apm->fmt));
685 }
686
cras_apm_list_get_format(struct cras_apm * apm)687 struct cras_audio_format *cras_apm_list_get_format(struct cras_apm *apm)
688 {
689 return &apm->fmt;
690 }
691
cras_apm_list_get_use_tuned_settings(struct cras_apm * apm)692 bool cras_apm_list_get_use_tuned_settings(struct cras_apm *apm)
693 {
694 /* If input and output devices in AEC use case, plus that a
695 * tuned setting is provided. */
696 return apm->is_aec_use_case && (aec_ini || apm_ini);
697 }
698
cras_apm_list_set_aec_dump(struct cras_apm_list * list,void * dev_ptr,int start,int fd)699 void cras_apm_list_set_aec_dump(struct cras_apm_list *list, void *dev_ptr,
700 int start, int fd)
701 {
702 struct cras_apm *apm;
703 char file_name[256];
704 int rc;
705 FILE *handle;
706
707 DL_SEARCH_SCALAR(list->apms, apm, dev_ptr, dev_ptr);
708 if (apm == NULL)
709 return;
710
711 if (start) {
712 handle = fdopen(fd, "w");
713 if (handle == NULL) {
714 syslog(LOG_ERR, "Create dump handle fail, errno %d",
715 errno);
716 return;
717 }
718 /* webrtc apm will own the FILE handle and close it. */
719 rc = webrtc_apm_aec_dump(apm->apm_ptr, &apm->work_queue, start,
720 handle);
721 if (rc)
722 syslog(LOG_ERR, "Fail to dump debug file %s, rc %d",
723 file_name, rc);
724 } else {
725 rc = webrtc_apm_aec_dump(apm->apm_ptr, &apm->work_queue, 0,
726 NULL);
727 if (rc)
728 syslog(LOG_ERR, "Failed to stop apm debug, rc %d", rc);
729 }
730 }
731