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