1 /* 2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include <stdlib.h> 12 //#include <string.h> 13 14 #include "echo_control_mobile.h" 15 #include "aecm_core.h" 16 #include "ring_buffer.h" 17 #ifdef AEC_DEBUG 18 #include <stdio.h> 19 #endif 20 #ifdef MAC_IPHONE_PRINT 21 #include <time.h> 22 #include <stdio.h> 23 #elif defined ARM_WINM_LOG 24 #include "windows.h" 25 extern HANDLE logFile; 26 #endif 27 28 #define BUF_SIZE_FRAMES 50 // buffer size (frames) 29 // Maximum length of resampled signal. Must be an integer multiple of frames 30 // (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN 31 // The factor of 2 handles wb, and the + 1 is as a safety margin 32 #define MAX_RESAMP_LEN (5 * FRAME_LEN) 33 34 static const size_t kBufSizeSamp = BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples) 35 static const int kSampMsNb = 8; // samples per ms in nb 36 // Target suppression levels for nlp modes 37 // log{0.001, 0.00001, 0.00000001} 38 static const int kInitCheck = 42; 39 40 typedef struct 41 { 42 int sampFreq; 43 int scSampFreq; 44 short bufSizeStart; 45 int knownDelay; 46 47 // Stores the last frame added to the farend buffer 48 short farendOld[2][FRAME_LEN]; 49 short initFlag; // indicates if AEC has been initialized 50 51 // Variables used for averaging far end buffer size 52 short counter; 53 short sum; 54 short firstVal; 55 short checkBufSizeCtr; 56 57 // Variables used for delay shifts 58 short msInSndCardBuf; 59 short filtDelay; 60 int timeForDelayChange; 61 int ECstartup; 62 int checkBuffSize; 63 int delayChange; 64 short lastDelayDiff; 65 66 WebRtc_Word16 echoMode; 67 68 #ifdef AEC_DEBUG 69 FILE *bufFile; 70 FILE *delayFile; 71 FILE *preCompFile; 72 FILE *postCompFile; 73 #endif // AEC_DEBUG 74 // Structures 75 void *farendBuf; 76 77 int lastError; 78 79 AecmCore_t *aecmCore; 80 } aecmob_t; 81 82 // Estimates delay to set the position of the farend buffer read pointer 83 // (controlled by knownDelay) 84 static int WebRtcAecm_EstBufDelay(aecmob_t *aecmInst, short msInSndCardBuf); 85 86 // Stuffs the farend buffer if the estimated delay is too large 87 static int WebRtcAecm_DelayComp(aecmob_t *aecmInst); 88 89 WebRtc_Word32 WebRtcAecm_Create(void **aecmInst) 90 { 91 aecmob_t *aecm; 92 if (aecmInst == NULL) 93 { 94 return -1; 95 } 96 97 aecm = malloc(sizeof(aecmob_t)); 98 *aecmInst = aecm; 99 if (aecm == NULL) 100 { 101 return -1; 102 } 103 104 if (WebRtcAecm_CreateCore(&aecm->aecmCore) == -1) 105 { 106 WebRtcAecm_Free(aecm); 107 aecm = NULL; 108 return -1; 109 } 110 111 if (WebRtc_CreateBuffer(&aecm->farendBuf, kBufSizeSamp, 112 sizeof(int16_t)) == -1) 113 { 114 WebRtcAecm_Free(aecm); 115 aecm = NULL; 116 return -1; 117 } 118 119 aecm->initFlag = 0; 120 aecm->lastError = 0; 121 122 #ifdef AEC_DEBUG 123 aecm->aecmCore->farFile = fopen("aecFar.pcm","wb"); 124 aecm->aecmCore->nearFile = fopen("aecNear.pcm","wb"); 125 aecm->aecmCore->outFile = fopen("aecOut.pcm","wb"); 126 //aecm->aecmCore->outLpFile = fopen("aecOutLp.pcm","wb"); 127 128 aecm->bufFile = fopen("aecBuf.dat", "wb"); 129 aecm->delayFile = fopen("aecDelay.dat", "wb"); 130 aecm->preCompFile = fopen("preComp.pcm", "wb"); 131 aecm->postCompFile = fopen("postComp.pcm", "wb"); 132 #endif // AEC_DEBUG 133 return 0; 134 } 135 136 WebRtc_Word32 WebRtcAecm_Free(void *aecmInst) 137 { 138 aecmob_t *aecm = aecmInst; 139 140 if (aecm == NULL) 141 { 142 return -1; 143 } 144 145 #ifdef AEC_DEBUG 146 fclose(aecm->aecmCore->farFile); 147 fclose(aecm->aecmCore->nearFile); 148 fclose(aecm->aecmCore->outFile); 149 //fclose(aecm->aecmCore->outLpFile); 150 151 fclose(aecm->bufFile); 152 fclose(aecm->delayFile); 153 fclose(aecm->preCompFile); 154 fclose(aecm->postCompFile); 155 #endif // AEC_DEBUG 156 WebRtcAecm_FreeCore(aecm->aecmCore); 157 WebRtc_FreeBuffer(aecm->farendBuf); 158 free(aecm); 159 160 return 0; 161 } 162 163 WebRtc_Word32 WebRtcAecm_Init(void *aecmInst, WebRtc_Word32 sampFreq) 164 { 165 aecmob_t *aecm = aecmInst; 166 AecmConfig aecConfig; 167 168 if (aecm == NULL) 169 { 170 return -1; 171 } 172 173 if (sampFreq != 8000 && sampFreq != 16000) 174 { 175 aecm->lastError = AECM_BAD_PARAMETER_ERROR; 176 return -1; 177 } 178 aecm->sampFreq = sampFreq; 179 180 // Initialize AECM core 181 if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1) 182 { 183 aecm->lastError = AECM_UNSPECIFIED_ERROR; 184 return -1; 185 } 186 187 // Initialize farend buffer 188 if (WebRtc_InitBuffer(aecm->farendBuf) == -1) 189 { 190 aecm->lastError = AECM_UNSPECIFIED_ERROR; 191 return -1; 192 } 193 194 aecm->initFlag = kInitCheck; // indicates that initialization has been done 195 196 aecm->delayChange = 1; 197 198 aecm->sum = 0; 199 aecm->counter = 0; 200 aecm->checkBuffSize = 1; 201 aecm->firstVal = 0; 202 203 aecm->ECstartup = 1; 204 aecm->bufSizeStart = 0; 205 aecm->checkBufSizeCtr = 0; 206 aecm->filtDelay = 0; 207 aecm->timeForDelayChange = 0; 208 aecm->knownDelay = 0; 209 aecm->lastDelayDiff = 0; 210 211 memset(&aecm->farendOld[0][0], 0, 160); 212 213 // Default settings. 214 aecConfig.cngMode = AecmTrue; 215 aecConfig.echoMode = 3; 216 217 if (WebRtcAecm_set_config(aecm, aecConfig) == -1) 218 { 219 aecm->lastError = AECM_UNSPECIFIED_ERROR; 220 return -1; 221 } 222 223 return 0; 224 } 225 226 WebRtc_Word32 WebRtcAecm_BufferFarend(void *aecmInst, const WebRtc_Word16 *farend, 227 WebRtc_Word16 nrOfSamples) 228 { 229 aecmob_t *aecm = aecmInst; 230 WebRtc_Word32 retVal = 0; 231 232 if (aecm == NULL) 233 { 234 return -1; 235 } 236 237 if (farend == NULL) 238 { 239 aecm->lastError = AECM_NULL_POINTER_ERROR; 240 return -1; 241 } 242 243 if (aecm->initFlag != kInitCheck) 244 { 245 aecm->lastError = AECM_UNINITIALIZED_ERROR; 246 return -1; 247 } 248 249 if (nrOfSamples != 80 && nrOfSamples != 160) 250 { 251 aecm->lastError = AECM_BAD_PARAMETER_ERROR; 252 return -1; 253 } 254 255 // TODO: Is this really a good idea? 256 if (!aecm->ECstartup) 257 { 258 WebRtcAecm_DelayComp(aecm); 259 } 260 261 WebRtc_WriteBuffer(aecm->farendBuf, farend, (size_t) nrOfSamples); 262 263 return retVal; 264 } 265 266 WebRtc_Word32 WebRtcAecm_Process(void *aecmInst, const WebRtc_Word16 *nearendNoisy, 267 const WebRtc_Word16 *nearendClean, WebRtc_Word16 *out, 268 WebRtc_Word16 nrOfSamples, WebRtc_Word16 msInSndCardBuf) 269 { 270 aecmob_t *aecm = aecmInst; 271 WebRtc_Word32 retVal = 0; 272 short i; 273 short nmbrOfFilledBuffers; 274 short nBlocks10ms; 275 short nFrames; 276 #ifdef AEC_DEBUG 277 short msInAECBuf; 278 #endif 279 280 #ifdef ARM_WINM_LOG 281 __int64 freq, start, end, diff; 282 unsigned int milliseconds; 283 DWORD temp; 284 #elif defined MAC_IPHONE_PRINT 285 // double endtime = 0, starttime = 0; 286 struct timeval starttime; 287 struct timeval endtime; 288 static long int timeused = 0; 289 static int timecount = 0; 290 #endif 291 292 if (aecm == NULL) 293 { 294 return -1; 295 } 296 297 if (nearendNoisy == NULL) 298 { 299 aecm->lastError = AECM_NULL_POINTER_ERROR; 300 return -1; 301 } 302 303 if (out == NULL) 304 { 305 aecm->lastError = AECM_NULL_POINTER_ERROR; 306 return -1; 307 } 308 309 if (aecm->initFlag != kInitCheck) 310 { 311 aecm->lastError = AECM_UNINITIALIZED_ERROR; 312 return -1; 313 } 314 315 if (nrOfSamples != 80 && nrOfSamples != 160) 316 { 317 aecm->lastError = AECM_BAD_PARAMETER_ERROR; 318 return -1; 319 } 320 321 if (msInSndCardBuf < 0) 322 { 323 msInSndCardBuf = 0; 324 aecm->lastError = AECM_BAD_PARAMETER_WARNING; 325 retVal = -1; 326 } else if (msInSndCardBuf > 500) 327 { 328 msInSndCardBuf = 500; 329 aecm->lastError = AECM_BAD_PARAMETER_WARNING; 330 retVal = -1; 331 } 332 msInSndCardBuf += 10; 333 aecm->msInSndCardBuf = msInSndCardBuf; 334 335 nFrames = nrOfSamples / FRAME_LEN; 336 nBlocks10ms = nFrames / aecm->aecmCore->mult; 337 338 if (aecm->ECstartup) 339 { 340 if (nearendClean == NULL) 341 { 342 memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples); 343 } else 344 { 345 memcpy(out, nearendClean, sizeof(short) * nrOfSamples); 346 } 347 348 nmbrOfFilledBuffers = 349 (short) WebRtc_available_read(aecm->farendBuf) / FRAME_LEN; 350 // The AECM is in the start up mode 351 // AECM is disabled until the soundcard buffer and farend buffers are OK 352 353 // Mechanism to ensure that the soundcard buffer is reasonably stable. 354 if (aecm->checkBuffSize) 355 { 356 aecm->checkBufSizeCtr++; 357 // Before we fill up the far end buffer we require the amount of data on the 358 // sound card to be stable (+/-8 ms) compared to the first value. This 359 // comparison is made during the following 4 consecutive frames. If it seems 360 // to be stable then we start to fill up the far end buffer. 361 362 if (aecm->counter == 0) 363 { 364 aecm->firstVal = aecm->msInSndCardBuf; 365 aecm->sum = 0; 366 } 367 368 if (abs(aecm->firstVal - aecm->msInSndCardBuf) 369 < WEBRTC_SPL_MAX(0.2 * aecm->msInSndCardBuf, kSampMsNb)) 370 { 371 aecm->sum += aecm->msInSndCardBuf; 372 aecm->counter++; 373 } else 374 { 375 aecm->counter = 0; 376 } 377 378 if (aecm->counter * nBlocks10ms >= 6) 379 { 380 // The farend buffer size is determined in blocks of 80 samples 381 // Use 75% of the average value of the soundcard buffer 382 aecm->bufSizeStart 383 = WEBRTC_SPL_MIN((3 * aecm->sum 384 * aecm->aecmCore->mult) / (aecm->counter * 40), BUF_SIZE_FRAMES); 385 // buffersize has now been determined 386 aecm->checkBuffSize = 0; 387 } 388 389 if (aecm->checkBufSizeCtr * nBlocks10ms > 50) 390 { 391 // for really bad sound cards, don't disable echocanceller for more than 0.5 sec 392 aecm->bufSizeStart = WEBRTC_SPL_MIN((3 * aecm->msInSndCardBuf 393 * aecm->aecmCore->mult) / 40, BUF_SIZE_FRAMES); 394 aecm->checkBuffSize = 0; 395 } 396 } 397 398 // if checkBuffSize changed in the if-statement above 399 if (!aecm->checkBuffSize) 400 { 401 // soundcard buffer is now reasonably stable 402 // When the far end buffer is filled with approximately the same amount of 403 // data as the amount on the sound card we end the start up phase and start 404 // to cancel echoes. 405 406 if (nmbrOfFilledBuffers == aecm->bufSizeStart) 407 { 408 aecm->ECstartup = 0; // Enable the AECM 409 } else if (nmbrOfFilledBuffers > aecm->bufSizeStart) 410 { 411 WebRtc_MoveReadPtr(aecm->farendBuf, 412 (int) WebRtc_available_read(aecm->farendBuf) 413 - (int) aecm->bufSizeStart * FRAME_LEN); 414 aecm->ECstartup = 0; 415 } 416 } 417 418 } else 419 { 420 // AECM is enabled 421 422 // Note only 1 block supported for nb and 2 blocks for wb 423 for (i = 0; i < nFrames; i++) 424 { 425 int16_t farend[FRAME_LEN]; 426 const int16_t* farend_ptr = NULL; 427 428 nmbrOfFilledBuffers = 429 (short) WebRtc_available_read(aecm->farendBuf) / FRAME_LEN; 430 431 // Check that there is data in the far end buffer 432 if (nmbrOfFilledBuffers > 0) 433 { 434 // Get the next 80 samples from the farend buffer 435 WebRtc_ReadBuffer(aecm->farendBuf, (void**) &farend_ptr, farend, 436 FRAME_LEN); 437 438 // Always store the last frame for use when we run out of data 439 memcpy(&(aecm->farendOld[i][0]), farend_ptr, 440 FRAME_LEN * sizeof(short)); 441 } else 442 { 443 // We have no data so we use the last played frame 444 memcpy(farend, &(aecm->farendOld[i][0]), FRAME_LEN * sizeof(short)); 445 farend_ptr = farend; 446 } 447 448 // Call buffer delay estimator when all data is extracted, 449 // i,e. i = 0 for NB and i = 1 for WB 450 if ((i == 0 && aecm->sampFreq == 8000) || (i == 1 && aecm->sampFreq == 16000)) 451 { 452 WebRtcAecm_EstBufDelay(aecm, aecm->msInSndCardBuf); 453 } 454 455 #ifdef ARM_WINM_LOG 456 // measure tick start 457 QueryPerformanceFrequency((LARGE_INTEGER*)&freq); 458 QueryPerformanceCounter((LARGE_INTEGER*)&start); 459 #elif defined MAC_IPHONE_PRINT 460 // starttime = clock()/(double)CLOCKS_PER_SEC; 461 gettimeofday(&starttime, NULL); 462 #endif 463 // Call the AECM 464 /*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i], 465 &out[FRAME_LEN * i], aecm->knownDelay);*/ 466 if (nearendClean == NULL) 467 { 468 if (WebRtcAecm_ProcessFrame(aecm->aecmCore, 469 farend_ptr, 470 &nearendNoisy[FRAME_LEN * i], 471 NULL, 472 &out[FRAME_LEN * i]) == -1) 473 { 474 return -1; 475 } 476 } else 477 { 478 if (WebRtcAecm_ProcessFrame(aecm->aecmCore, 479 farend_ptr, 480 &nearendNoisy[FRAME_LEN * i], 481 &nearendClean[FRAME_LEN * i], 482 &out[FRAME_LEN * i]) == -1) 483 { 484 return -1; 485 } 486 } 487 488 #ifdef ARM_WINM_LOG 489 490 // measure tick end 491 QueryPerformanceCounter((LARGE_INTEGER*)&end); 492 493 if(end > start) 494 { 495 diff = ((end - start) * 1000) / (freq/1000); 496 milliseconds = (unsigned int)(diff & 0xffffffff); 497 WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL); 498 } 499 #elif defined MAC_IPHONE_PRINT 500 // endtime = clock()/(double)CLOCKS_PER_SEC; 501 // printf("%f\n", endtime - starttime); 502 503 gettimeofday(&endtime, NULL); 504 505 if( endtime.tv_usec > starttime.tv_usec) 506 { 507 timeused += endtime.tv_usec - starttime.tv_usec; 508 } else 509 { 510 timeused += endtime.tv_usec + 1000000 - starttime.tv_usec; 511 } 512 513 if(++timecount == 1000) 514 { 515 timecount = 0; 516 printf("AEC: %ld\n", timeused); 517 timeused = 0; 518 } 519 #endif 520 521 } 522 } 523 524 #ifdef AEC_DEBUG 525 msInAECBuf = (short) WebRtc_available_read(aecm->farendBuf) / 526 (kSampMsNb * aecm->aecmCore->mult); 527 fwrite(&msInAECBuf, 2, 1, aecm->bufFile); 528 fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile); 529 #endif 530 531 return retVal; 532 } 533 534 WebRtc_Word32 WebRtcAecm_set_config(void *aecmInst, AecmConfig config) 535 { 536 aecmob_t *aecm = aecmInst; 537 538 if (aecm == NULL) 539 { 540 return -1; 541 } 542 543 if (aecm->initFlag != kInitCheck) 544 { 545 aecm->lastError = AECM_UNINITIALIZED_ERROR; 546 return -1; 547 } 548 549 if (config.cngMode != AecmFalse && config.cngMode != AecmTrue) 550 { 551 aecm->lastError = AECM_BAD_PARAMETER_ERROR; 552 return -1; 553 } 554 aecm->aecmCore->cngMode = config.cngMode; 555 556 if (config.echoMode < 0 || config.echoMode > 4) 557 { 558 aecm->lastError = AECM_BAD_PARAMETER_ERROR; 559 return -1; 560 } 561 aecm->echoMode = config.echoMode; 562 563 if (aecm->echoMode == 0) 564 { 565 aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 3; 566 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 3; 567 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 3; 568 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 3; 569 aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 3) 570 - (SUPGAIN_ERROR_PARAM_B >> 3); 571 aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 3) 572 - (SUPGAIN_ERROR_PARAM_D >> 3); 573 } else if (aecm->echoMode == 1) 574 { 575 aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 2; 576 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 2; 577 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 2; 578 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 2; 579 aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 2) 580 - (SUPGAIN_ERROR_PARAM_B >> 2); 581 aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 2) 582 - (SUPGAIN_ERROR_PARAM_D >> 2); 583 } else if (aecm->echoMode == 2) 584 { 585 aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 1; 586 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 1; 587 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 1; 588 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 1; 589 aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 1) 590 - (SUPGAIN_ERROR_PARAM_B >> 1); 591 aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 1) 592 - (SUPGAIN_ERROR_PARAM_D >> 1); 593 } else if (aecm->echoMode == 3) 594 { 595 aecm->aecmCore->supGain = SUPGAIN_DEFAULT; 596 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT; 597 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A; 598 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D; 599 aecm->aecmCore->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B; 600 aecm->aecmCore->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D; 601 } else if (aecm->echoMode == 4) 602 { 603 aecm->aecmCore->supGain = SUPGAIN_DEFAULT << 1; 604 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT << 1; 605 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A << 1; 606 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D << 1; 607 aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A << 1) 608 - (SUPGAIN_ERROR_PARAM_B << 1); 609 aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B << 1) 610 - (SUPGAIN_ERROR_PARAM_D << 1); 611 } 612 613 return 0; 614 } 615 616 WebRtc_Word32 WebRtcAecm_get_config(void *aecmInst, AecmConfig *config) 617 { 618 aecmob_t *aecm = aecmInst; 619 620 if (aecm == NULL) 621 { 622 return -1; 623 } 624 625 if (config == NULL) 626 { 627 aecm->lastError = AECM_NULL_POINTER_ERROR; 628 return -1; 629 } 630 631 if (aecm->initFlag != kInitCheck) 632 { 633 aecm->lastError = AECM_UNINITIALIZED_ERROR; 634 return -1; 635 } 636 637 config->cngMode = aecm->aecmCore->cngMode; 638 config->echoMode = aecm->echoMode; 639 640 return 0; 641 } 642 643 WebRtc_Word32 WebRtcAecm_InitEchoPath(void* aecmInst, 644 const void* echo_path, 645 size_t size_bytes) 646 { 647 aecmob_t *aecm = aecmInst; 648 const WebRtc_Word16* echo_path_ptr = echo_path; 649 650 if ((aecm == NULL) || (echo_path == NULL)) 651 { 652 aecm->lastError = AECM_NULL_POINTER_ERROR; 653 return -1; 654 } 655 if (size_bytes != WebRtcAecm_echo_path_size_bytes()) 656 { 657 // Input channel size does not match the size of AECM 658 aecm->lastError = AECM_BAD_PARAMETER_ERROR; 659 return -1; 660 } 661 if (aecm->initFlag != kInitCheck) 662 { 663 aecm->lastError = AECM_UNINITIALIZED_ERROR; 664 return -1; 665 } 666 667 WebRtcAecm_InitEchoPathCore(aecm->aecmCore, echo_path_ptr); 668 669 return 0; 670 } 671 672 WebRtc_Word32 WebRtcAecm_GetEchoPath(void* aecmInst, 673 void* echo_path, 674 size_t size_bytes) 675 { 676 aecmob_t *aecm = aecmInst; 677 WebRtc_Word16* echo_path_ptr = echo_path; 678 679 if ((aecm == NULL) || (echo_path == NULL)) 680 { 681 aecm->lastError = AECM_NULL_POINTER_ERROR; 682 return -1; 683 } 684 if (size_bytes != WebRtcAecm_echo_path_size_bytes()) 685 { 686 // Input channel size does not match the size of AECM 687 aecm->lastError = AECM_BAD_PARAMETER_ERROR; 688 return -1; 689 } 690 if (aecm->initFlag != kInitCheck) 691 { 692 aecm->lastError = AECM_UNINITIALIZED_ERROR; 693 return -1; 694 } 695 696 memcpy(echo_path_ptr, aecm->aecmCore->channelStored, size_bytes); 697 return 0; 698 } 699 700 size_t WebRtcAecm_echo_path_size_bytes() 701 { 702 return (PART_LEN1 * sizeof(WebRtc_Word16)); 703 } 704 705 WebRtc_Word32 WebRtcAecm_get_version(WebRtc_Word8 *versionStr, WebRtc_Word16 len) 706 { 707 const char version[] = "AECM 1.2.0"; 708 const short versionLen = (short)strlen(version) + 1; // +1 for null-termination 709 710 if (versionStr == NULL) 711 { 712 return -1; 713 } 714 715 if (versionLen > len) 716 { 717 return -1; 718 } 719 720 strncpy(versionStr, version, versionLen); 721 return 0; 722 } 723 724 WebRtc_Word32 WebRtcAecm_get_error_code(void *aecmInst) 725 { 726 aecmob_t *aecm = aecmInst; 727 728 if (aecm == NULL) 729 { 730 return -1; 731 } 732 733 return aecm->lastError; 734 } 735 736 static int WebRtcAecm_EstBufDelay(aecmob_t *aecm, short msInSndCardBuf) 737 { 738 short delayNew, nSampSndCard; 739 short nSampFar = (short) WebRtc_available_read(aecm->farendBuf); 740 short diff; 741 742 nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult; 743 744 delayNew = nSampSndCard - nSampFar; 745 746 if (delayNew < FRAME_LEN) 747 { 748 WebRtc_MoveReadPtr(aecm->farendBuf, FRAME_LEN); 749 delayNew += FRAME_LEN; 750 } 751 752 aecm->filtDelay = WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10); 753 754 diff = aecm->filtDelay - aecm->knownDelay; 755 if (diff > 224) 756 { 757 if (aecm->lastDelayDiff < 96) 758 { 759 aecm->timeForDelayChange = 0; 760 } else 761 { 762 aecm->timeForDelayChange++; 763 } 764 } else if (diff < 96 && aecm->knownDelay > 0) 765 { 766 if (aecm->lastDelayDiff > 224) 767 { 768 aecm->timeForDelayChange = 0; 769 } else 770 { 771 aecm->timeForDelayChange++; 772 } 773 } else 774 { 775 aecm->timeForDelayChange = 0; 776 } 777 aecm->lastDelayDiff = diff; 778 779 if (aecm->timeForDelayChange > 25) 780 { 781 aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0); 782 } 783 return 0; 784 } 785 786 static int WebRtcAecm_DelayComp(aecmob_t *aecm) 787 { 788 int nSampFar = (int) WebRtc_available_read(aecm->farendBuf); 789 int nSampSndCard, delayNew, nSampAdd; 790 const int maxStuffSamp = 10 * FRAME_LEN; 791 792 nSampSndCard = aecm->msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult; 793 delayNew = nSampSndCard - nSampFar; 794 795 if (delayNew > FAR_BUF_LEN - FRAME_LEN * aecm->aecmCore->mult) 796 { 797 // The difference of the buffer sizes is larger than the maximum 798 // allowed known delay. Compensate by stuffing the buffer. 799 nSampAdd = (int)(WEBRTC_SPL_MAX(((nSampSndCard >> 1) - nSampFar), 800 FRAME_LEN)); 801 nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp); 802 803 WebRtc_MoveReadPtr(aecm->farendBuf, -nSampAdd); 804 aecm->delayChange = 1; // the delay needs to be updated 805 } 806 807 return 0; 808 } 809