blob: ab99c9330bd45ced6bd2dd00bc1dc785536d51c2 [file] [log] [blame]
Amit Pundire6732bb2020-09-28 12:43:59 +05301/*
2 * Copyright (C) 2019 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// clang-format off
18/*
19 * Typical AEC signal flow:
20 *
21 * Microphone Audio
22 * Timestamps
23 * +--------------------------------------+
24 * | | +---------------+
25 * | Microphone +---------------+ | | |
26 * O|====== | Audio | Sample Rate | +-------> |
27 * (from . +--+ Samples | + | | |
28 * mic . +==================> Format |==============> |
29 * codec) . | Conversion | | | Cleaned
30 * O|====== | (if required) | | Acoustic | Audio
31 * +---------------+ | Echo | Samples
32 * | Canceller |===================>
33 * | (AEC) |
34 * Reference +---------------+ | |
35 * Audio | Sample Rate | | |
36 * Samples | + | | |
37 * +=============> Format |==============> |
38 * | | Conversion | | |
39 * | | (if required) | +-------> |
40 * | +---------------+ | | |
41 * | | +---------------+
42 * | +-------------------------------+
43 * | | Reference Audio
44 * | | Timestamps
45 * | |
46 * +--+----+---------+ AUDIO CAPTURE
47 * | Speaker |
48 * +------------+ Audio/Timestamp +---------------------------------------------------------------------------+
49 * | Buffer |
50 * +--^----^---------+ AUDIO PLAYBACK
51 * | |
52 * | |
53 * | |
54 * | |
55 * |\ | |
56 * | +-+ | |
57 * (to | | +-----C----+
58 * speaker | | | | Playback
59 * codec) | | <=====+================================================================+ Audio
60 * | +-+ Samples
61 * |/
62 *
63 */
64// clang-format on
65
66#define LOG_TAG "audio_hw_aec"
67// #define LOG_NDEBUG 0
68
69#include <audio_utils/primitives.h>
70#include <stdio.h>
71#include <inttypes.h>
72#include <errno.h>
73#include <malloc.h>
74#include <sys/time.h>
75#include <tinyalsa/asoundlib.h>
76#include <unistd.h>
77#include <log/log.h>
78#include "audio_aec.h"
79
80#ifdef AEC_HAL
81#include "audio_aec_process.h"
82#else
83#define aec_spk_mic_init(...) ((int)0)
84#define aec_spk_mic_reset(...) ((void)0)
85#define aec_spk_mic_process(...) ((int32_t)0)
86#define aec_spk_mic_release(...) ((void)0)
87#endif
88
89#define MAX_TIMESTAMP_DIFF_USEC 200000
90
91#define MAX_READ_WAIT_TIME_MSEC 80
92
93uint64_t timespec_to_usec(struct timespec ts) {
94 return (ts.tv_sec * 1e6L + ts.tv_nsec/1000);
95}
96
97void get_reference_audio_in_place(struct aec_t *aec, size_t frames) {
98 if (aec->num_reference_channels == aec->spk_num_channels) {
99 /* Reference count equals speaker channels, nothing to do here. */
100 return;
101 } else if (aec->num_reference_channels != 1) {
102 /* We don't have a rule for non-mono references, show error on log */
103 ALOGE("Invalid reference count - must be 1 or match number of playback channels!");
104 return;
105 }
106 int16_t *src_Nch = &aec->spk_buf_playback_format[0];
107 int16_t *dst_1ch = &aec->spk_buf_playback_format[0];
108 int32_t num_channels = (int32_t)aec->spk_num_channels;
109 size_t frame, ch;
110 for (frame = 0; frame < frames; frame++) {
111 int32_t acc = 0;
112 for (ch = 0; ch < aec->spk_num_channels; ch++) {
113 acc += src_Nch[ch];
114 }
115 *dst_1ch++ = clamp16(acc/num_channels);
116 src_Nch += aec->spk_num_channels;
117 }
118}
119
120void print_queue_status_to_log(struct aec_t *aec, bool write_side) {
121 ssize_t q1 = fifo_available_to_read(aec->spk_fifo);
122 ssize_t q2 = fifo_available_to_read(aec->ts_fifo);
123
124 ALOGV("Queue available %s: Spk %zd (count %zd) TS %zd (count %zd)",
125 (write_side) ? "(POST-WRITE)" : "(PRE-READ)",
126 q1, q1/aec->spk_frame_size_bytes/PLAYBACK_PERIOD_SIZE,
127 q2, q2/sizeof(struct aec_info));
128}
129
130void flush_aec_fifos(struct aec_t *aec) {
131 if (aec == NULL) {
132 return;
133 }
134 if (aec->spk_fifo != NULL) {
135 ALOGV("Flushing AEC Spk FIFO...");
136 fifo_flush(aec->spk_fifo);
137 }
138 if (aec->ts_fifo != NULL) {
139 ALOGV("Flushing AEC Timestamp FIFO...");
140 fifo_flush(aec->ts_fifo);
141 }
142 /* Reset FIFO read-write offset tracker */
143 aec->read_write_diff_bytes = 0;
144}
145
146void aec_set_spk_running_no_lock(struct aec_t* aec, bool state) {
147 aec->spk_running = state;
148}
149
150bool aec_get_spk_running_no_lock(struct aec_t* aec) {
151 return aec->spk_running;
152}
153
154void destroy_aec_reference_config_no_lock(struct aec_t* aec) {
155 if (!aec->spk_initialized) {
156 return;
157 }
158 aec_set_spk_running_no_lock(aec, false);
159 fifo_release(aec->spk_fifo);
160 fifo_release(aec->ts_fifo);
161 memset(&aec->last_spk_info, 0, sizeof(struct aec_info));
162 aec->spk_initialized = false;
163}
164
165void destroy_aec_mic_config_no_lock(struct aec_t* aec) {
166 if (!aec->mic_initialized) {
167 return;
168 }
169 release_resampler(aec->spk_resampler);
170 free(aec->mic_buf);
171 free(aec->spk_buf);
172 free(aec->spk_buf_playback_format);
173 free(aec->spk_buf_resampler_out);
174 memset(&aec->last_mic_info, 0, sizeof(struct aec_info));
175 aec->mic_initialized = false;
176}
177
178struct aec_t *init_aec_interface() {
179 ALOGV("%s enter", __func__);
180 struct aec_t *aec = (struct aec_t *)calloc(1, sizeof(struct aec_t));
181 if (aec == NULL) {
182 ALOGE("Failed to allocate memory for AEC interface!");
183 } else {
184 pthread_mutex_init(&aec->lock, NULL);
185 }
186
187 ALOGV("%s exit", __func__);
188 return aec;
189}
190
191void release_aec_interface(struct aec_t *aec) {
192 ALOGV("%s enter", __func__);
193 pthread_mutex_lock(&aec->lock);
194 destroy_aec_mic_config_no_lock(aec);
195 destroy_aec_reference_config_no_lock(aec);
196 pthread_mutex_unlock(&aec->lock);
197 free(aec);
198 ALOGV("%s exit", __func__);
199}
200
201int init_aec(int sampling_rate, int num_reference_channels,
202 int num_microphone_channels, struct aec_t **aec_ptr) {
203 ALOGV("%s enter", __func__);
204 int ret = 0;
205 int aec_ret = aec_spk_mic_init(
206 sampling_rate,
207 num_reference_channels,
208 num_microphone_channels);
209 if (aec_ret) {
210 ALOGE("AEC object failed to initialize!");
211 ret = -EINVAL;
212 }
213 struct aec_t *aec = init_aec_interface();
214 if (!ret) {
215 aec->num_reference_channels = num_reference_channels;
216 /* Set defaults, will be overridden by settings in init_aec_(mic|referece_config) */
217 /* Capture uses 2-ch, 32-bit frames */
218 aec->mic_sampling_rate = CAPTURE_CODEC_SAMPLING_RATE;
219 aec->mic_frame_size_bytes = CHANNEL_STEREO * sizeof(int32_t);
220 aec->mic_num_channels = CHANNEL_STEREO;
221
222 /* Playback uses 2-ch, 16-bit frames */
223 aec->spk_sampling_rate = PLAYBACK_CODEC_SAMPLING_RATE;
224 aec->spk_frame_size_bytes = CHANNEL_STEREO * sizeof(int16_t);
225 aec->spk_num_channels = CHANNEL_STEREO;
226 }
227
228 (*aec_ptr) = aec;
229 ALOGV("%s exit", __func__);
230 return ret;
231}
232
233void release_aec(struct aec_t *aec) {
234 ALOGV("%s enter", __func__);
235 if (aec == NULL) {
236 return;
237 }
238 release_aec_interface(aec);
239 aec_spk_mic_release();
240 ALOGV("%s exit", __func__);
241}
242
243int init_aec_reference_config(struct aec_t *aec, struct alsa_stream_out *out) {
244 ALOGV("%s enter", __func__);
245 if (!aec) {
246 ALOGE("AEC: No valid interface found!");
247 return -EINVAL;
248 }
249
250 int ret = 0;
251 pthread_mutex_lock(&aec->lock);
252 if (aec->spk_initialized) {
253 destroy_aec_reference_config_no_lock(aec);
254 }
255
256 aec->spk_fifo = fifo_init(
257 out->config.period_count * out->config.period_size *
258 audio_stream_out_frame_size(&out->stream),
259 false /* reader_throttles_writer */);
260 if (aec->spk_fifo == NULL) {
261 ALOGE("AEC: Speaker loopback FIFO Init failed!");
262 ret = -EINVAL;
263 goto exit;
264 }
265 aec->ts_fifo = fifo_init(
266 out->config.period_count * sizeof(struct aec_info),
267 false /* reader_throttles_writer */);
268 if (aec->ts_fifo == NULL) {
269 ALOGE("AEC: Speaker timestamp FIFO Init failed!");
270 ret = -EINVAL;
271 fifo_release(aec->spk_fifo);
272 goto exit;
273 }
274
275 aec->spk_sampling_rate = out->config.rate;
276 aec->spk_frame_size_bytes = audio_stream_out_frame_size(&out->stream);
277 aec->spk_num_channels = out->config.channels;
278 aec->spk_initialized = true;
279exit:
280 pthread_mutex_unlock(&aec->lock);
281 ALOGV("%s exit", __func__);
282 return ret;
283}
284
285void destroy_aec_reference_config(struct aec_t* aec) {
286 ALOGV("%s enter", __func__);
287 if (aec == NULL) {
288 ALOGV("%s exit", __func__);
289 return;
290 }
291 pthread_mutex_lock(&aec->lock);
292 destroy_aec_reference_config_no_lock(aec);
293 pthread_mutex_unlock(&aec->lock);
294 ALOGV("%s exit", __func__);
295}
296
297int write_to_reference_fifo(struct aec_t* aec, void* buffer, struct aec_info* info) {
298 ALOGV("%s enter", __func__);
299 int ret = 0;
300 size_t bytes = info->bytes;
301
302 /* Write audio samples to FIFO */
303 ssize_t written_bytes = fifo_write(aec->spk_fifo, buffer, bytes);
304 if (written_bytes != bytes) {
305 ALOGE("Could only write %zu of %zu bytes", written_bytes, bytes);
306 ret = -ENOMEM;
307 }
308
309 /* Write timestamp to FIFO */
310 info->bytes = written_bytes;
311 ALOGV("Speaker timestamp: %ld s, %ld nsec", info->timestamp.tv_sec, info->timestamp.tv_nsec);
312 ssize_t ts_bytes = fifo_write(aec->ts_fifo, info, sizeof(struct aec_info));
313 ALOGV("Wrote TS bytes: %zu", ts_bytes);
314 print_queue_status_to_log(aec, true);
315 ALOGV("%s exit", __func__);
316 return ret;
317}
318
319void get_spk_timestamp(struct aec_t* aec, ssize_t read_bytes, uint64_t* spk_time) {
320 *spk_time = 0;
321 uint64_t spk_time_offset = 0;
322 float usec_per_byte = 1E6 / ((float)(aec->spk_frame_size_bytes * aec->spk_sampling_rate));
323 if (aec->read_write_diff_bytes < 0) {
324 /* We're still reading a previous write packet. (We only need the first sample's timestamp,
325 * so even if we straddle packets we only care about the first one)
326 * So we just use the previous timestamp, with an appropriate offset
327 * based on the number of bytes remaining to be read from that write packet. */
328 spk_time_offset = (aec->last_spk_info.bytes + aec->read_write_diff_bytes) * usec_per_byte;
329 ALOGV("Reusing previous timestamp, calculated offset (usec) %" PRIu64, spk_time_offset);
330 } else {
331 /* If read_write_diff_bytes > 0, there are no new writes, so there won't be timestamps in
332 * the FIFO, and the check below will fail. */
333 if (!fifo_available_to_read(aec->ts_fifo)) {
334 ALOGE("Timestamp error: no new timestamps!");
335 return;
336 }
337 /* We just read valid data, so if we're here, we should have a valid timestamp to use. */
338 ssize_t ts_bytes = fifo_read(aec->ts_fifo, &aec->last_spk_info, sizeof(struct aec_info));
339 ALOGV("Read TS bytes: %zd, expected %zu", ts_bytes, sizeof(struct aec_info));
340 aec->read_write_diff_bytes -= aec->last_spk_info.bytes;
341 }
342
343 *spk_time = timespec_to_usec(aec->last_spk_info.timestamp) + spk_time_offset;
344
345 aec->read_write_diff_bytes += read_bytes;
346 struct aec_info spk_info = aec->last_spk_info;
347 while (aec->read_write_diff_bytes > 0) {
348 /* If read_write_diff_bytes > 0, it means that there are more write packet timestamps
349 * in FIFO (since there we read more valid data the size of the current timestamp's
350 * packet). Keep reading timestamps from FIFO to get to the most recent one. */
351 if (!fifo_available_to_read(aec->ts_fifo)) {
352 /* There are no more timestamps, we have the most recent one. */
353 ALOGV("At the end of timestamp FIFO, breaking...");
354 break;
355 }
356 fifo_read(aec->ts_fifo, &spk_info, sizeof(struct aec_info));
357 ALOGV("Fast-forwarded timestamp by %zd bytes, remaining bytes: %zd,"
358 " new timestamp (usec) %" PRIu64,
359 spk_info.bytes, aec->read_write_diff_bytes, timespec_to_usec(spk_info.timestamp));
360 aec->read_write_diff_bytes -= spk_info.bytes;
361 }
362 aec->last_spk_info = spk_info;
363}
364
365int get_reference_samples(struct aec_t* aec, void* buffer, struct aec_info* info) {
366 ALOGV("%s enter", __func__);
367
368 if (!aec->spk_initialized) {
369 ALOGE("%s called with no reference initialized", __func__);
370 return -EINVAL;
371 }
372
373 size_t bytes = info->bytes;
374 const size_t frames = bytes / aec->mic_frame_size_bytes;
375 const size_t sample_rate_ratio = aec->spk_sampling_rate / aec->mic_sampling_rate;
376
377 /* Read audio samples from FIFO */
378 const size_t req_bytes = frames * sample_rate_ratio * aec->spk_frame_size_bytes;
379 ssize_t available_bytes = 0;
380 unsigned int wait_count = MAX_READ_WAIT_TIME_MSEC;
381 while (true) {
382 available_bytes = fifo_available_to_read(aec->spk_fifo);
383 if (available_bytes >= req_bytes) {
384 break;
385 } else if (available_bytes < 0) {
386 ALOGE("fifo_read returned code %zu ", available_bytes);
387 return -ENOMEM;
388 }
389
390 ALOGV("Sleeping, required bytes: %zu, available bytes: %zd", req_bytes, available_bytes);
391 usleep(1000);
392 if ((wait_count--) == 0) {
393 ALOGE("Timed out waiting for read from reference FIFO");
394 return -ETIMEDOUT;
395 }
396 }
397
398 const size_t read_bytes = fifo_read(aec->spk_fifo, aec->spk_buf_playback_format, req_bytes);
399
400 /* Get timestamp*/
401 get_spk_timestamp(aec, read_bytes, &info->timestamp_usec);
402
403 /* Get reference - could be mono, downmixed from multichannel.
404 * Reference stored at spk_buf_playback_format */
405 const size_t resampler_in_frames = frames * sample_rate_ratio;
406 get_reference_audio_in_place(aec, resampler_in_frames);
407
408 int16_t* resampler_out_buf;
409 /* Resample to mic sampling rate (16-bit resampler) */
410 if (aec->spk_resampler != NULL) {
411 size_t in_frame_count = resampler_in_frames;
412 size_t out_frame_count = frames;
413 aec->spk_resampler->resample_from_input(aec->spk_resampler, aec->spk_buf_playback_format,
414 &in_frame_count, aec->spk_buf_resampler_out,
415 &out_frame_count);
416 resampler_out_buf = aec->spk_buf_resampler_out;
417 } else {
418 if (sample_rate_ratio != 1) {
419 ALOGE("Speaker sample rate %d, mic sample rate %d but no resampler defined!",
420 aec->spk_sampling_rate, aec->mic_sampling_rate);
421 }
422 resampler_out_buf = aec->spk_buf_playback_format;
423 }
424
425 /* Convert to 32 bit */
426 int16_t* src16 = resampler_out_buf;
427 int32_t* dst32 = buffer;
428 size_t frame, ch;
429 for (frame = 0; frame < frames; frame++) {
430 for (ch = 0; ch < aec->num_reference_channels; ch++) {
431 *dst32++ = ((int32_t)*src16++) << 16;
432 }
433 }
434
435 info->bytes = bytes;
436
437 ALOGV("%s exit", __func__);
438 return 0;
439}
440
441int init_aec_mic_config(struct aec_t *aec, struct alsa_stream_in *in) {
442 ALOGV("%s enter", __func__);
443#if DEBUG_AEC
444 remove("/data/local/traces/aec_in.pcm");
445 remove("/data/local/traces/aec_out.pcm");
446 remove("/data/local/traces/aec_ref.pcm");
447 remove("/data/local/traces/aec_timestamps.txt");
448#endif /* #if DEBUG_AEC */
449
450 if (!aec) {
451 ALOGE("AEC: No valid interface found!");
452 return -EINVAL;
453 }
454
455 int ret = 0;
456 pthread_mutex_lock(&aec->lock);
457 if (aec->mic_initialized) {
458 destroy_aec_mic_config_no_lock(aec);
459 }
460 aec->mic_sampling_rate = in->config.rate;
461 aec->mic_frame_size_bytes = audio_stream_in_frame_size(&in->stream);
462 aec->mic_num_channels = in->config.channels;
463
464 aec->mic_buf_size_bytes = in->config.period_size * audio_stream_in_frame_size(&in->stream);
465 aec->mic_buf = (int32_t *)malloc(aec->mic_buf_size_bytes);
466 if (aec->mic_buf == NULL) {
467 ret = -ENOMEM;
468 goto exit;
469 }
470 memset(aec->mic_buf, 0, aec->mic_buf_size_bytes);
471 /* Reference buffer is the same number of frames as mic,
472 * only with a different number of channels in the frame. */
473 aec->spk_buf_size_bytes = in->config.period_size * aec->spk_frame_size_bytes;
474 aec->spk_buf = (int32_t *)malloc(aec->spk_buf_size_bytes);
475 if (aec->spk_buf == NULL) {
476 ret = -ENOMEM;
477 goto exit_1;
478 }
479 memset(aec->spk_buf, 0, aec->spk_buf_size_bytes);
480
481 /* Pre-resampler buffer */
482 size_t spk_frame_out_format_bytes = aec->spk_sampling_rate / aec->mic_sampling_rate *
483 aec->spk_buf_size_bytes;
484 aec->spk_buf_playback_format = (int16_t *)malloc(spk_frame_out_format_bytes);
485 if (aec->spk_buf_playback_format == NULL) {
486 ret = -ENOMEM;
487 goto exit_2;
488 }
489 /* Resampler is 16-bit */
490 aec->spk_buf_resampler_out = (int16_t *)malloc(aec->spk_buf_size_bytes);
491 if (aec->spk_buf_resampler_out == NULL) {
492 ret = -ENOMEM;
493 goto exit_3;
494 }
495
496 /* Don't use resampler if it's not required */
497 if (in->config.rate == aec->spk_sampling_rate) {
498 aec->spk_resampler = NULL;
499 } else {
500 int resampler_ret = create_resampler(
501 aec->spk_sampling_rate, in->config.rate, aec->num_reference_channels,
502 RESAMPLER_QUALITY_MAX - 1, /* MAX - 1 is the real max */
503 NULL, /* resampler_buffer_provider */
504 &aec->spk_resampler);
505 if (resampler_ret) {
506 ALOGE("AEC: Resampler initialization failed! Error code %d", resampler_ret);
507 ret = resampler_ret;
508 goto exit_4;
509 }
510 }
511
512 flush_aec_fifos(aec);
513 aec_spk_mic_reset();
514 aec->mic_initialized = true;
515
516exit:
517 pthread_mutex_unlock(&aec->lock);
518 ALOGV("%s exit", __func__);
519 return ret;
520
521exit_4:
522 free(aec->spk_buf_resampler_out);
523exit_3:
524 free(aec->spk_buf_playback_format);
525exit_2:
526 free(aec->spk_buf);
527exit_1:
528 free(aec->mic_buf);
529 pthread_mutex_unlock(&aec->lock);
530 ALOGV("%s exit", __func__);
531 return ret;
532}
533
534void aec_set_spk_running(struct aec_t *aec, bool state) {
535 ALOGV("%s enter", __func__);
536 pthread_mutex_lock(&aec->lock);
537 aec_set_spk_running_no_lock(aec, state);
538 pthread_mutex_unlock(&aec->lock);
539 ALOGV("%s exit", __func__);
540}
541
542bool aec_get_spk_running(struct aec_t *aec) {
543 ALOGV("%s enter", __func__);
544 pthread_mutex_lock(&aec->lock);
545 bool state = aec_get_spk_running_no_lock(aec);
546 pthread_mutex_unlock(&aec->lock);
547 ALOGV("%s exit", __func__);
548 return state;
549}
550
551void destroy_aec_mic_config(struct aec_t* aec) {
552 ALOGV("%s enter", __func__);
553 if (aec == NULL) {
554 ALOGV("%s exit", __func__);
555 return;
556 }
557
558 pthread_mutex_lock(&aec->lock);
559 destroy_aec_mic_config_no_lock(aec);
560 pthread_mutex_unlock(&aec->lock);
561 ALOGV("%s exit", __func__);
562}
563
564#ifdef AEC_HAL
565int process_aec(struct aec_t *aec, void* buffer, struct aec_info *info) {
566 ALOGV("%s enter", __func__);
567 int ret = 0;
568
569 if (aec == NULL) {
570 ALOGE("AEC: Interface uninitialized! Cannot process.");
571 return -EINVAL;
572 }
573
574 if ((!aec->mic_initialized) || (!aec->spk_initialized)) {
575 ALOGE("%s called with initialization: mic: %d, spk: %d", __func__, aec->mic_initialized,
576 aec->spk_initialized);
577 return -EINVAL;
578 }
579
580 size_t bytes = info->bytes;
581
582 size_t frame_size = aec->mic_frame_size_bytes;
583 size_t in_frames = bytes / frame_size;
584
585 /* Copy raw mic samples to AEC input buffer */
586 memcpy(aec->mic_buf, buffer, bytes);
587
588 uint64_t mic_time = timespec_to_usec(info->timestamp);
589 uint64_t spk_time = 0;
590
591 /*
592 * Only run AEC if there is speaker playback.
593 * The first time speaker state changes to running, flush FIFOs, so we're not stuck
594 * processing stale reference input.
595 */
596 bool spk_running = aec_get_spk_running(aec);
597
598 if (!spk_running) {
599 /* No new playback samples, so don't run AEC.
600 * 'buffer' already contains input samples. */
601 ALOGV("Speaker not running, skipping AEC..");
602 goto exit;
603 }
604
605 if (!aec->prev_spk_running) {
606 flush_aec_fifos(aec);
607 }
608
609 /* If there's no data in FIFO, exit */
610 if (fifo_available_to_read(aec->spk_fifo) <= 0) {
611 ALOGV("Echo reference buffer empty, zeroing reference....");
612 goto exit;
613 }
614
615 print_queue_status_to_log(aec, false);
616
617 /* Get reference, with format and sample rate required by AEC */
618 struct aec_info spk_info;
619 spk_info.bytes = bytes;
620 int ref_ret = get_reference_samples(aec, aec->spk_buf, &spk_info);
621 spk_time = spk_info.timestamp_usec;
622
623 if (ref_ret) {
624 ALOGE("get_reference_samples returned code %d", ref_ret);
625 ret = -ENOMEM;
626 goto exit;
627 }
628
629 int64_t time_diff = (mic_time > spk_time) ? (mic_time - spk_time) : (spk_time - mic_time);
630 if ((spk_time == 0) || (mic_time == 0) || (time_diff > MAX_TIMESTAMP_DIFF_USEC)) {
631 ALOGV("Speaker-mic timestamps diverged, skipping AEC");
632 flush_aec_fifos(aec);
633 aec_spk_mic_reset();
634 goto exit;
635 }
636
637 ALOGV("Mic time: %"PRIu64", spk time: %"PRIu64, mic_time, spk_time);
638
639 /*
640 * AEC processing call - output stored at 'buffer'
641 */
642 int32_t aec_status = aec_spk_mic_process(
643 aec->spk_buf, spk_time,
644 aec->mic_buf, mic_time,
645 in_frames,
646 buffer);
647
648 if (!aec_status) {
649 ALOGE("AEC processing failed!");
650 ret = -EINVAL;
651 }
652
653exit:
654 aec->prev_spk_running = spk_running;
655 ALOGV("Mic time: %"PRIu64", spk time: %"PRIu64, mic_time, spk_time);
656 if (ret) {
657 /* Best we can do is copy over the raw mic signal */
658 memcpy(buffer, aec->mic_buf, bytes);
659 flush_aec_fifos(aec);
660 aec_spk_mic_reset();
661 }
662
663#if DEBUG_AEC
664 /* ref data is 32-bit at this point */
665 size_t ref_bytes = in_frames*aec->num_reference_channels*sizeof(int32_t);
666
667 FILE *fp_in = fopen("/data/local/traces/aec_in.pcm", "a+");
668 if (fp_in) {
669 fwrite((char *)aec->mic_buf, 1, bytes, fp_in);
670 fclose(fp_in);
671 } else {
672 ALOGE("AEC debug: Could not open file aec_in.pcm!");
673 }
674 FILE *fp_out = fopen("/data/local/traces/aec_out.pcm", "a+");
675 if (fp_out) {
676 fwrite((char *)buffer, 1, bytes, fp_out);
677 fclose(fp_out);
678 } else {
679 ALOGE("AEC debug: Could not open file aec_out.pcm!");
680 }
681 FILE *fp_ref = fopen("/data/local/traces/aec_ref.pcm", "a+");
682 if (fp_ref) {
683 fwrite((char *)aec->spk_buf, 1, ref_bytes, fp_ref);
684 fclose(fp_ref);
685 } else {
686 ALOGE("AEC debug: Could not open file aec_ref.pcm!");
687 }
688 FILE *fp_ts = fopen("/data/local/traces/aec_timestamps.txt", "a+");
689 if (fp_ts) {
690 fprintf(fp_ts, "%"PRIu64",%"PRIu64"\n", mic_time, spk_time);
691 fclose(fp_ts);
692 } else {
693 ALOGE("AEC debug: Could not open file aec_timestamps.txt!");
694 }
695#endif /* #if DEBUG_AEC */
696 ALOGV("%s exit", __func__);
697 return ret;
698}
699
700#endif /*#ifdef AEC_HAL*/