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