Amit Pundir | fec74f6 | 2020-09-28 12:43:59 +0530 | [diff] [blame] | 1 | /* |
| 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 | |
| 93 | uint64_t timespec_to_usec(struct timespec ts) { |
| 94 | return (ts.tv_sec * 1e6L + ts.tv_nsec/1000); |
| 95 | } |
| 96 | |
| 97 | void 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 | |
| 120 | void 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 | |
| 130 | void 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 | |
| 146 | void aec_set_spk_running_no_lock(struct aec_t* aec, bool state) { |
| 147 | aec->spk_running = state; |
| 148 | } |
| 149 | |
| 150 | bool aec_get_spk_running_no_lock(struct aec_t* aec) { |
| 151 | return aec->spk_running; |
| 152 | } |
| 153 | |
| 154 | void 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 | |
| 165 | void 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 | |
| 178 | struct 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 | |
| 191 | void 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 | |
| 201 | int 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 | |
| 233 | void 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 | |
| 243 | int 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; |
| 279 | exit: |
| 280 | pthread_mutex_unlock(&aec->lock); |
| 281 | ALOGV("%s exit", __func__); |
| 282 | return ret; |
| 283 | } |
| 284 | |
| 285 | void 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 | |
| 297 | int 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 | |
| 319 | void 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 | |
| 365 | int 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 | |
| 441 | int 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 | |
| 516 | exit: |
| 517 | pthread_mutex_unlock(&aec->lock); |
| 518 | ALOGV("%s exit", __func__); |
| 519 | return ret; |
| 520 | |
| 521 | exit_4: |
| 522 | free(aec->spk_buf_resampler_out); |
| 523 | exit_3: |
| 524 | free(aec->spk_buf_playback_format); |
| 525 | exit_2: |
| 526 | free(aec->spk_buf); |
| 527 | exit_1: |
| 528 | free(aec->mic_buf); |
| 529 | pthread_mutex_unlock(&aec->lock); |
| 530 | ALOGV("%s exit", __func__); |
| 531 | return ret; |
| 532 | } |
| 533 | |
| 534 | void 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 | |
| 542 | bool 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 | |
| 551 | void 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 |
| 565 | int 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 | |
| 653 | exit: |
| 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*/ |