protected override void PerformEchoCancellation(short[] recorded, short[] played, short[] outFrame) { // ks 11/2/11 - This seems to be more-or-less the order in which things are processed in the WebRtc audio_processing_impl.cc file. _highPassFilter.Filter(recorded); if (_enableAgc) { _agc.WebRtcAgc_AddFarend(played, (short)played.Length); gain_control_AnalyzeCaptureAudio(recorded); } if (_enableAec) { _aec.ProcessFrame(recorded, played, outFrame, 0); } else { Buffer.BlockCopy(recorded, 0, outFrame, 0, SamplesPerFrame * sizeof(short)); } if (_enableDenoise) { // ks 11/14/11 - The noise suppressor only supports 10 ms blocks. I might be able to fix that, // but this is easier for now. _ns.ProcessFrame(outFrame, 0, outFrame, 0); _ns.ProcessFrame(outFrame, _recordedAudioFormat.SamplesPer10Ms, outFrame, _recordedAudioFormat.SamplesPer10Ms); } if (_enableAgc) { gain_control_ProcessCaptureAudio(outFrame); } }
public void WebRtcAec_Process(short[] nearend, short streamDelayMs, int skew) { int nrOfSamples = nearend.Length; short msInSndCardBuf = streamDelayMs; var farend = new short[aecConfig.SamplesPerFrame]; short nmbrOfFilledBuffers; // number of samples == 160 for SWB input if (nrOfSamples != 80 && nrOfSamples != 160) { throw new ArgumentException(); } // Check for valid pointers based on sampling rate if (sampFreq == 32000) { throw new ArgumentException(); } if (msInSndCardBuf < 0) { msInSndCardBuf = 0; //throw new ArgumentException();//todo:warning } else if (msInSndCardBuf > 500) { msInSndCardBuf = 500; //todo:warning } msInSndCardBuf += 10; this.msInSndCardBuf = msInSndCardBuf; if (skewMode) { //if (aecpc->skewFrCtr < 25) { // aecpc->skewFrCtr++; //} //else { // retVal = WebRtcAec_GetSkew(aecpc->resampler, skew, &aecpc->skew); // if (retVal == -1) { // aecpc->skew = 0; // aecpc->lastError = AEC_BAD_PARAMETER_WARNING; // } // aecpc->skew /= aecpc->sampFactor*nrOfSamples; // if (aecpc->skew < 1.0e-3 && aecpc->skew > -1.0e-3) { // aecpc->resample = kAecFalse; // } // else { // aecpc->resample = kAecTrue; // } // if (aecpc->skew < minSkewEst) { // aecpc->skew = minSkewEst; // } // else if (aecpc->skew > maxSkewEst) { // aecpc->skew = maxSkewEst; // } //} } var nFrames = (short)(nrOfSamples / aecConfig.SamplesPerFrame); var nBlocks10Ms = (short)(nFrames / aec.mult); WebRtcUtil.WriteDebugMessage(String.Format("(C#) AEC 01 ECstartup = {0}", ECstartup)); if (ECstartup) { nmbrOfFilledBuffers = (short)(farendBuf.get_buffer_size() / aecConfig.SamplesPerFrame); // The AEC is in the start up mode // AEC is disabled until the soundcard buffer and farend buffers are OK // Mechanism to ensure that the soundcard buffer is reasonably stable. if (checkBuffSize) { checkBufSizeCtr++; // Before we fill up the far end buffer we require the amount of data on the // sound card to be stable (+/-8 ms) compared to the first value. This // comparison is made during the following 4 consecutive frames. If it seems // to be stable then we start to fill up the far end buffer. if (counter == 0) { firstVal = this.msInSndCardBuf; sum = 0; } if (Math.Abs(firstVal - this.msInSndCardBuf) < WebRtcUtil.WEBRTC_SPL_MAX((int)(0.2 * this.msInSndCardBuf), WebRtcConstants.sampMsNb)) { sum += this.msInSndCardBuf; counter++; } else { counter = 0; } if (counter * nBlocks10Ms >= 6) { // The farend buffer size is determined in blocks of 80 samples // Use 75% of the average value of the soundcard buffer bufSizeStart = (short)WebRtcUtil.WEBRTC_SPL_MIN((int)(0.75 * (sum * aec.mult) / (counter * 10)), WebRtcConstants.BUF_SIZE_FRAMES); // buffersize has now been determined checkBuffSize = false; } if (checkBufSizeCtr * nBlocks10Ms > 50) { // for really bad sound cards, don't disable echocanceller for more than 0.5 sec bufSizeStart = (short)WebRtcUtil.WEBRTC_SPL_MIN((int)(0.75 * (this.msInSndCardBuf * aec.mult) / 10), WebRtcConstants.BUF_SIZE_FRAMES); checkBuffSize = false; } } // if checkBuffSize changed in the if-statement above if (!checkBuffSize) { // soundcard buffer is now reasonably stable // When the far end buffer is filled with approximately the same amount of // data as the amount on the sound card we end the start up phase and start // to cancel echoes. if (nmbrOfFilledBuffers == bufSizeStart) { ECstartup = false; // Enable the AEC } else if (nmbrOfFilledBuffers > bufSizeStart) { farendBuf.Flush(farendBuf.get_buffer_size() - bufSizeStart * aecConfig.SamplesPerFrame); ECstartup = false; } } } else { // AEC is enabled // Note only 1 block supported for nb and 2 blocks for wb for (int i = 0; i < nFrames; i++) { nmbrOfFilledBuffers = (short)(farendBuf.get_buffer_size() / aecConfig.SamplesPerFrame); // Check that there is data in the far end buffer if (nmbrOfFilledBuffers > 0) { // Get the next 80 samples from the farend buffer farendBuf.Read(farend, aecConfig.SamplesPerFrame); // Always store the last frame for use when we run out of data farendOld[i] = farend; } else { // We have no data so we use the last played frame farend = farendOld[i]; } // Call buffer delay estimator when all data is extracted, // i.e. i = 0 for NB and i = 1 for WB or SWB if ((i == 0 && splitSampFreq == 8000) || (i == 1 && (splitSampFreq == 16000))) { EstBufDelay(this.msInSndCardBuf); } // Call the AEC var nearend80 = new short[aecConfig.SamplesPerFrame]; Buffer.BlockCopy(nearend, aecConfig.SamplesPerFrame * i * sizeof(short), nearend80, 0, aecConfig.SamplesPerFrame * sizeof(short)); aec.ProcessFrame(nearend80, farend, nearend80, knownDelay); Buffer.BlockCopy(nearend80, 0, nearend, aecConfig.SamplesPerFrame * i * sizeof(short), aecConfig.SamplesPerFrame * sizeof(short)); } } }