Акустическое эхо-подавление (AEC)

В этом примере показано, как применить адаптивные фильтры к акустической эхоподавке (AEC).

Автор (ы): Скотт К. Дуглас

Введение

Акустическое эхо-подавление важно для аудио телеконференций, когда необходима одновременная связь (или полнодуплексная передача) речи. В акустическом эхоподавлении измеренный сигнал микрофона$d(n)$ содержит два сигнала:

  • Речевой сигнал ближнего конца $v(n)$

  • Речевой сигнал на дальнем конце с эхом $\widehat{d}(n)$

Цель состоит в том, чтобы удалить речевой сигнал с обратной связью на дальнем конце из микрофонного сигнала, так что передается только речевой сигнал на ближнем конце. В этом примере есть несколько звуковых клипов, поэтому теперь можно настроить громкость компьютера.

Импульсная характеристика помещения

Сначала необходимо смоделировать акустику пути громкоговорителя к микрофону, где расположен громкоговоритель. Используйте длинный фильтр конечной импульсной характеристики, чтобы описать характеристики помещения. Следующий код генерирует случайную импульсную характеристику, которая не отличается от того, что выставляет конференц-зал. Примите частоту выборки системы 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). Этот алгоритм очень полезен, когда импульсная характеристика системы, которая будет идентифицирована, долга. 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)

Поскольку у вас есть доступ как к речевым сигналам ближнего, так и дальнего конца, можно вычислить улучшение эхо возврата потерь (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