Акустическая отмена эха (AEC)

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

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

Введение

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

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

  • Дальний конец повторил речевой сигнал $\widehat{d}(n)$

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

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

Сначала необходимо смоделировать акустику пути прохождения сигнала громкоговорителя к микрофону, где спикерфон расположен. Используйте длинный конечный фильтр импульсной характеристики, чтобы описать характеристики комнаты. Следующий код генерирует случайную импульсную характеристику, которая мало чем отличается от того, что показал бы конференц-зал. Примите системную частоту дискретизации 16 000 Гц.

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 = dsp.TimeScope('SampleRate', fs, ...
                    '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  = dsp.TimeScope('SampleRate', fs, ...
                    '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        = dsp.TimeScope('SampleRate', fs,...
                    '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   = dsp.TimeScope(4, fs, ...
                'LayoutDimensions', [4,1], ...
                '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), которое является сглаживавшей мерой суммы (в дБ), что эхо было ослаблено. Из графика заметьте, что вы достигли приблизительно ERLE на 35 дБ в конце периода сходимости.

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);

Эффекты различных значений размера шага

Чтобы получить более быструю сходимость, можно попытаться использовать большее значение размера шага. Однако это увеличение вызывает другой эффект: адаптивный фильтр является "misadjusted", в то время как докладчик почти конца говорит. Слушайте то, что происходит, когда вы выбираете размер шага, который на 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