• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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