В этом примере показано, как применять адаптивные фильтры к акустической эхо-компенсации (AD).
Автор (авторы): Скотт К. Дуглас
Акустическая эхо-компенсация важна для аудиотелеконференции, когда необходима одновременная связь (или полнодуплексная передача) речи. При акустическом эхо-подавлении измеренный микрофонный сигнал
содержит два сигнала:
Речевой сигнал ближнего конца 
Эхо-речевой сигнал дальнего конца 
Целью является удаление эхо-сигнала дальнего конца из сигнала микрофона так, чтобы передавался только речевой сигнал ближнего конца. В этом примере есть звуковые клипы, поэтому может потребоваться изменить громкость компьютера.
Сначала нужно смоделировать акустику тракта сигнала громкоговоритель-микрофон, где находится громкоговоритель. Используйте фильтр с длинной конечной импульсной характеристикой для описания характеристик помещения. Следующий код генерирует случайную импульсную характеристику, которая не похожа на то, что показывал бы конференц-зал. Предположим, частота дискретизации системы составляет 16000 Гц.
fs = 16000; M = fs/2 + 1; frameSize = 2048; [B,A] = cheby2(4,20,[0.1 0.7]); impulseResponseGenerator = dsp.IIRFilter('Numerator', [zeros(1,6) B], ... 'Denominator', A); FVT = fvtool(impulseResponseGenerator); % Analyze the filter FVT.Color = [1 1 1];

roomImpulseResponse = impulseResponseGenerator( ... (log(0.99*rand(1,M)+0.01).*sign(randn(1,M)).*exp(-0.002*(1:M)))'); roomImpulseResponse = roomImpulseResponse/norm(roomImpulseResponse)*4; room = dsp.FIRFilter('Numerator', roomImpulseResponse'); fig = figure; plot(0:1/fs:0.5, roomImpulseResponse); xlabel('Time (s)'); ylabel('Amplitude'); title('Room Impulse Response'); fig.Color = [1 1 1];

Пользователь системы телеконференций обычно располагается вблизи микрофона системы. Вот как звучит мужская речь у микрофона.
load nearspeech player = audioDeviceWriter('SupportVariableSizeInput', true, ... 'BufferSize', 512, 'SampleRate', fs); nearSpeechSrc = dsp.SignalSource('Signal',v,'SamplesPerFrame',frameSize); nearSpeechScope = timescope('SampleRate', fs, 'TimeSpanSource','Property',... 'TimeSpan', 35, 'TimeSpanOverrunAction', 'Scroll', ... 'YLimits', [-1.5 1.5], ... 'BufferLength', length(v), ... 'Title', 'Near-End Speech Signal', ... 'ShowGrid', true); % Stream processing loop while(~isDone(nearSpeechSrc)) % Extract the speech samples from the input signal nearSpeech = nearSpeechSrc(); % Send the speech samples to the output audio device player(nearSpeech); % Plot the signal nearSpeechScope(nearSpeech); end release(nearSpeechScope);

В системе телеконференцсвязи голос выходит из громкоговорителя, отскакивает в комнате и затем забирается микрофоном системы. Слушайте, как звучит речь, если она подбирается к микрофону без присутствия речи на ближнем конце.
load farspeech farSpeechSrc = dsp.SignalSource('Signal',x,'SamplesPerFrame',frameSize); farSpeechSink = dsp.SignalSink; farSpeechScope = timescope('SampleRate', fs, 'TimeSpanSource','Property',... 'TimeSpan', 35, 'TimeSpanOverrunAction', 'Scroll', ... 'YLimits', [-0.5 0.5], ... 'BufferLength', length(x), ... 'Title', 'Far-End Speech Signal', ... 'ShowGrid', true); % Stream processing loop while(~isDone(farSpeechSrc)) % Extract the speech samples from the input signal farSpeech = farSpeechSrc(); % Add the room effect to the far-end speech signal farSpeechEcho = room(farSpeech); % Send the speech samples to the output audio device player(farSpeechEcho); % Plot the signal farSpeechScope(farSpeech); % Log the signal for further processing farSpeechSink(farSpeechEcho); end release(farSpeechScope);

Сигнал в микрофоне содержит как речь ближнего конца, так и речь дальнего конца, которая была эхолотирована по всему помещению. Целью акустического эхоподавителя является отмена речи на дальнем конце, так что только речь на ближнем конце передается обратно слушателю на дальнем конце.
reset(nearSpeechSrc); farSpeechEchoSrc = dsp.SignalSource('Signal', farSpeechSink.Buffer, ... 'SamplesPerFrame', frameSize); micSink = dsp.SignalSink; micScope = timescope('SampleRate', fs,'TimeSpanSource','Property',... 'TimeSpan', 35, 'TimeSpanOverrunAction', 'Scroll',... 'YLimits', [-1 1], ... 'BufferLength', length(x), ... 'Title', 'Microphone Signal', ... 'ShowGrid', true); % Stream processing loop while(~isDone(farSpeechEchoSrc)) % Microphone signal = echoed far-end + near-end + noise micSignal = farSpeechEchoSrc() + nearSpeechSrc() + ... 0.001*randn(frameSize,1); % Send the speech samples to the output audio device player(micSignal); % Plot the signal micScope(micSignal); % Log the signal micSink(micSignal); end release(micScope);

Алгоритм в этом примере - адаптивный фильтр частотной области (FDAF). Этот алгоритм очень полезен, когда импульсная характеристика идентифицируемой системы велика. FDAF использует метод быстрой свертки для вычисления выходного сигнала и обновления фильтра. Это вычисление выполняется быстро в MATLAB ®. Он также имеет быструю сходимость за счет нормализации размера шага частотного бина. Выберите некоторые начальные параметры фильтра и проверьте, насколько хорошо в сигнале ошибки отменяется речь на дальнем конце.
% Construct the Frequency-Domain Adaptive Filter echoCanceller = dsp.FrequencyDomainAdaptiveFilter('Length', 2048, ... 'StepSize', 0.025, ... 'InitialPower', 0.01, ... 'AveragingFactor', 0.98, ... 'Method', 'Unconstrained FDAF'); AECScope1 = timescope(4, fs, ... 'LayoutDimensions', [4,1],'TimeSpanSource','Property', ... 'TimeSpan', 35, 'TimeSpanOverrunAction', 'Scroll', ... 'BufferLength', length(x)); AECScope1.ActiveDisplay = 1; AECScope1.ShowGrid = true; AECScope1.YLimits = [-1.5 1.5]; AECScope1.Title = 'Near-End Speech Signal'; AECScope1.ActiveDisplay = 2; AECScope1.ShowGrid = true; AECScope1.YLimits = [-1.5 1.5]; AECScope1.Title = 'Microphone Signal'; AECScope1.ActiveDisplay = 3; AECScope1.ShowGrid = true; AECScope1.YLimits = [-1.5 1.5]; AECScope1.Title = 'Output of Acoustic Echo Canceller mu=0.025'; AECScope1.ActiveDisplay = 4; AECScope1.ShowGrid = true; AECScope1.YLimits = [0 50]; AECScope1.YLabel = 'ERLE (dB)'; AECScope1.Title = 'Echo Return Loss Enhancement mu=0.025'; % Near-end speech signal release(nearSpeechSrc); nearSpeechSrc.SamplesPerFrame = frameSize; % Far-end speech signal release(farSpeechSrc); farSpeechSrc.SamplesPerFrame = frameSize; % Far-end speech signal echoed by the room release(farSpeechEchoSrc); farSpeechEchoSrc.SamplesPerFrame = frameSize;
Так как у вас есть доступ как к речевым сигналам ближнего, так и дальнего конца, вы можете вычислить усиление обратной потери эхо-сигнала (ERLE), которое является сглаженной мерой величины (в дБ), что эхо-сигнал был ослаблен. На графике обратите внимание, что в конце периода сходимости достигнуто около 35 дБ ERLE.
diffAverager = dsp.FIRFilter('Numerator', ones(1,1024)); farEchoAverager = clone(diffAverager); setfilter(FVT,diffAverager); micSrc = dsp.SignalSource('Signal', micSink.Buffer, ... 'SamplesPerFrame', frameSize); % Stream processing loop - adaptive filter step size = 0.025 while(~isDone(nearSpeechSrc)) nearSpeech = nearSpeechSrc(); farSpeech = farSpeechSrc(); farSpeechEcho = farSpeechEchoSrc(); micSignal = micSrc(); % Apply FDAF [y,e] = echoCanceller(farSpeech, micSignal); % Send the speech samples to the output audio device player(e); % Compute ERLE erle = diffAverager((e-nearSpeech).^2)./ farEchoAverager(farSpeechEcho.^2); erledB = -10*log10(erle); % Plot near-end, far-end, microphone, AEC output and ERLE AECScope1(nearSpeech, micSignal, e, erledB); end release(AECScope1);


Для ускорения сходимости можно использовать большее значение размера шага. Однако это увеличение вызывает и другой эффект: адаптивный фильтр «неправильно настраивается», пока говорящий на ближнем конце говорит. Слушайте, что происходит, когда вы выбираете размер шага, который на 60% больше, чем раньше.
% Change the step size value in FDAF reset(echoCanceller); echoCanceller.StepSize = 0.04; AECScope2 = clone(AECScope1); AECScope2.ActiveDisplay = 3; AECScope2.Title = 'Output of Acoustic Echo Canceller mu=0.04'; AECScope2.ActiveDisplay = 4; AECScope2.Title = 'Echo Return Loss Enhancement mu=0.04'; reset(nearSpeechSrc); reset(farSpeechSrc); reset(farSpeechEchoSrc); reset(micSrc); reset(diffAverager); reset(farEchoAverager); % Stream processing loop - adaptive filter step size = 0.04 while(~isDone(nearSpeechSrc)) nearSpeech = nearSpeechSrc(); farSpeech = farSpeechSrc(); farSpeechEcho = farSpeechEchoSrc(); micSignal = micSrc(); % Apply FDAF [y,e] = echoCanceller(farSpeech, micSignal); % Send the speech samples to the output audio device player(e); % Compute ERLE erle = diffAverager((e-nearSpeech).^2)./ farEchoAverager(farSpeechEcho.^2); erledB = -10*log10(erle); % Plot near-end, far-end, microphone, AEC output and ERLE AECScope2(nearSpeech, micSignal, e, erledB); end release(nearSpeechSrc); release(farSpeechSrc); release(farSpeechEchoSrc); release(micSrc); release(diffAverager); release(farEchoAverager); release(echoCanceller); release(AECScope2);

При большем размере шага производительность ERLE не так хороша из-за неправильной настройки, введенной речью ближнего конца. Чтобы справиться с этой трудностью, акустические эхоподавители включают в себя схему обнаружения, чтобы определить, когда присутствует речь ближнего конца, и снизить значение размера шага в течение этих периодов. Без таких схем обнаружения производительность системы с большим размером шага не так хороша, как первая, что видно из графиков ERLE.
Традиционная FDAF численно эффективнее, чем адаптивная фильтрация во временной области для длинных импульсных откликов, но она накладывает высокую задержку, поскольку размер входного кадра должен быть кратен заданной длине фильтра. Это может быть неприемлемо для многих реальных приложений. Задержка может быть уменьшена путем использования разделенного FDAF, который разделяет импульсную характеристику фильтра на более короткие сегменты, применяет FDAF к каждому сегменту, а затем объединяет промежуточные результаты. Размер кадра в этом случае должен быть кратен длине раздела (блока), тем самым значительно уменьшая задержку для длинных импульсных откликов.
% Reduce the frame size from 2048 to 256 frameSize = 256; nearSpeechSrc.SamplesPerFrame = frameSize; farSpeechSrc.SamplesPerFrame = frameSize; farSpeechEchoSrc.SamplesPerFrame = frameSize; micSrc.SamplesPerFrame = frameSize; % Switch the echo canceller to Partitioned constrained FDAF echoCanceller.Method = 'Partitioned constrained FDAF'; % Set the block length to frameSize echoCanceller.BlockLength = frameSize; % Stream processing loop while(~isDone(nearSpeechSrc)) nearSpeech = nearSpeechSrc(); farSpeech = farSpeechSrc(); farSpeechEcho = farSpeechEchoSrc(); micSignal = micSrc(); % Apply FDAF [y,e] = echoCanceller(farSpeech, micSignal); % Send the speech samples to the output audio device player(e); % Compute ERLE erle = diffAverager((e-nearSpeech).^2)./ farEchoAverager(farSpeechEcho.^2); erledB = -10*log10(erle); % Plot near-end, far-end, microphone, AEC output and ERLE AECScope2(nearSpeech, micSignal, e, erledB); end
